aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--README.md15
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp2
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h2
-rw-r--r--src/blockchain_db/blockchain_db.cpp18
-rw-r--r--src/blockchain_db/blockchain_db.h4
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp67
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h4
-rw-r--r--src/blockchain_utilities/CMakeLists.txt33
-rw-r--r--src/blockchain_utilities/README.md7
-rw-r--r--src/blockchain_utilities/blockchain_converter.cpp306
-rw-r--r--src/blockchain_utilities/blockchain_dump.cpp20
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp15
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp40
-rw-r--r--src/blockchain_utilities/blockchain_utilities.h15
-rw-r--r--src/blockchain_utilities/blocksdat_file.cpp4
-rw-r--r--src/blockchain_utilities/blocksdat_file.h10
-rw-r--r--src/blockchain_utilities/bootstrap_file.cpp32
-rw-r--r--src/blockchain_utilities/bootstrap_file.h10
-rw-r--r--src/blockchain_utilities/fake_core.h64
-rw-r--r--src/common/json_util.h4
-rw-r--r--src/crypto/crypto-ops.c14
-rw-r--r--src/crypto/crypto-ops.h8
-rw-r--r--src/crypto/crypto.cpp6
-rw-r--r--src/crypto/crypto.h21
-rw-r--r--src/crypto/keccak.c6
-rw-r--r--src/crypto/keccak.h4
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/CMakeLists.txt3
-rw-r--r--src/cryptonote_core/blockchain.cpp575
-rw-r--r--src/cryptonote_core/blockchain.h50
-rw-r--r--src/cryptonote_core/blockchain_storage.cpp1952
-rw-r--r--src/cryptonote_core/blockchain_storage.h377
-rw-r--r--src/cryptonote_core/cryptonote_basic.h81
-rw-r--r--src/cryptonote_core/cryptonote_boost_serialization.h120
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp60
-rw-r--r--src/cryptonote_core/cryptonote_core.h21
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp251
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.h13
-rw-r--r--src/cryptonote_core/tx_pool.cpp79
-rw-r--r--src/cryptonote_core/tx_pool.h19
-rw-r--r--src/cryptonote_core/verification_context.h1
-rw-r--r--src/ringct/CMakeLists.txt59
-rw-r--r--src/ringct/rctCryptoOps.c221
-rw-r--r--src/ringct/rctCryptoOps.h37
-rw-r--r--src/ringct/rctOps.cpp475
-rw-r--r--src/ringct/rctOps.h171
-rw-r--r--src/ringct/rctSigs.cpp875
-rw-r--r--src/ringct/rctSigs.h150
-rw-r--r--src/ringct/rctTypes.cpp208
-rw-r--r--src/ringct/rctTypes.h405
-rw-r--r--src/rpc/core_rpc_server.cpp48
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h57
-rw-r--r--src/serialization/crypto.h1
-rw-r--r--src/serialization/serialization.h6
-rw-r--r--src/serialization/vector.h5
-rw-r--r--src/simplewallet/simplewallet.cpp151
-rw-r--r--src/simplewallet/simplewallet.h6
-rw-r--r--src/wallet/api/transaction_history.cpp5
-rw-r--r--src/wallet/api/wallet.cpp12
-rw-r--r--src/wallet/wallet2.cpp1059
-rw-r--r--src/wallet/wallet2.h143
-rw-r--r--src/wallet/wallet_rpc_server.cpp27
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h31
-rw-r--r--tests/core_proxy/core_proxy.h3
-rw-r--r--tests/core_tests/CMakeLists.txt6
-rw-r--r--tests/core_tests/block_validation.cpp6
-rw-r--r--tests/core_tests/chaingen.cpp16
-rw-r--r--tests/core_tests/chaingen.h13
-rw-r--r--tests/core_tests/chaingen_main.cpp29
-rw-r--r--tests/core_tests/chaingen_tests_list.h1
-rw-r--r--tests/core_tests/double_spend.inl3
-rw-r--r--tests/core_tests/integer_overflow.cpp3
-rw-r--r--tests/core_tests/rct.cpp499
-rw-r--r--tests/core_tests/rct.h264
-rw-r--r--tests/core_tests/transaction_tests.cpp26
-rw-r--r--tests/core_tests/tx_validation.cpp12
-rw-r--r--tests/core_tests/v2_tests.cpp11
-rw-r--r--tests/core_tests/v2_tests.h2
-rw-r--r--tests/functional_tests/transactions_flow_test.cpp2
-rw-r--r--tests/functional_tests/transactions_generation_from_blockchain.cpp2
-rw-r--r--tests/performance_tests/multi_tx_test_base.h3
-rw-r--r--tests/unit_tests/CMakeLists.txt5
-rw-r--r--tests/unit_tests/hardfork.cpp2
-rw-r--r--tests/unit_tests/output_selection.cpp103
-rw-r--r--tests/unit_tests/ringct.cpp1070
-rw-r--r--tests/unit_tests/serialization.cpp198
-rw-r--r--utils/conf/bitmonerod.conf7
-rw-r--r--utils/systemd/bitmonerod.service22
91 files changed, 7279 insertions, 3523 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b5589450b..675650b0f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -235,10 +235,6 @@ elseif (DATABASE STREQUAL "berkeleydb")
message(STATUS "Using Berkeley DB as default DB type")
add_definitions("-DDEFAULT_DB_TYPE=\"berkeley\"")
-elseif (DATABASE STREQUAL "memory")
- set(BLOCKCHAIN_DB DB_MEMORY)
- message(STATUS "Using Serialised In Memory as default DB type")
- add_definitions("-DDEFAULT_DB_TYPE=\"memory\"")
else()
die("Invalid database type: ${DATABASE}")
endif()
diff --git a/README.md b/README.md
index ea0e18963..eec743e78 100644
--- a/README.md
+++ b/README.md
@@ -216,16 +216,23 @@ foreground:
./bin/bitmonerod
-To run in background:
-
- ./bin/bitmonerod --log-file bitmonerod.log --detach
-
To list all available options, run `./bin/bitmonerod --help`. Options can be
specified either on the command line or in a configuration file passed by the
`--config-file` argument. To specify an option in the configuration file, add
a line with the syntax `argumentname=value`, where `argumentname` is the name
of the argument without the leading dashes, for example `log-level=1`.
+To run in background:
+
+ ./bin/bitmonerod --log-file bitmonerod.log --detach
+
+To run as a systemd service, copy
+[bitmonerod.service](utils/systemd/bitmonerod.service) to `/etc/systemd/system/` and
+[bitmonerod.conf](utils/conf/bitmonerod.conf) to `/etc/`. The [example
+service](utils/systemd/bitmonerod.service) assumes that the user `bitmonero` exists
+and its home is the data directory specified in the [example
+config](utils/conf/bitmonerod.conf).
+
## Internationalization
See README.i18n
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index dfa31aa72..70bb215d0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -91,6 +91,7 @@ endfunction ()
add_subdirectory(common)
add_subdirectory(crypto)
+add_subdirectory(ringct)
add_subdirectory(cryptonote_core)
add_subdirectory(blockchain_db)
add_subdirectory(mnemonics)
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index 4d759d157..4ec284e38 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -361,7 +361,7 @@ void BlockchainBDB::remove_transaction_data(const crypto::hash& tx_hash, const t
throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction"));
}
-void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time)
+void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment)
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open();
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index 6bc26ed3d..f8e9440bd 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -364,7 +364,7 @@ private:
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
- virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time);
+ virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment);
virtual void remove_output(const tx_out& tx_output);
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 68f635d18..7eb81d933 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -46,6 +46,7 @@ void BlockchainDB::pop_block()
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr)
{
+ bool miner_tx = false;
crypto::hash tx_hash;
if (!tx_hash_ptr)
{
@@ -67,6 +68,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
else if (tx_input.type() == typeid(txin_gen))
{
/* nothing to do here */
+ miner_tx = true;
}
else
{
@@ -90,7 +92,21 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
// we need the index
for (uint64_t i = 0; i < tx.vout.size(); ++i)
{
- amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time));
+ // miner v2 txes have their coinbase output in one single out to save space,
+ // and we store them as rct outputs with an identity mask
+ if (miner_tx && tx.version == 2)
+ {
+ cryptonote::tx_out vout = tx.vout[i];
+ rct::key commitment = rct::zeroCommit(vout.amount);
+ vout.amount = 0;
+ amount_output_indices.push_back(add_output(tx_hash, vout, i, tx.unlock_time,
+ &commitment));
+ }
+ else
+ {
+ amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
+ tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL));
+ }
}
add_tx_amount_output_indices(tx_id, amount_output_indices);
}
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 4c2157a39..d26080a3b 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -110,6 +110,7 @@ struct output_data_t
crypto::public_key pubkey; //!< the output's public key (for spend verification)
uint64_t unlock_time; //!< the output's unlock time (or height)
uint64_t height; //!< the height of the block which created the output
+ rct::key commitment; //!< the output's amount commitment (for spend verification)
};
#pragma pack(pop)
@@ -407,9 +408,10 @@ private:
* @param tx_output the output
* @param local_index index of the output in its transaction
* @param unlock_time unlock time/height of the output
+ * @param commitment the rct commitment to the output amount
* @return amount output index
*/
- virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) = 0;
+ virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) = 0;
/**
* @brief store amount output indices for a tx's outputs
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 0464ece59..a0a0a041d 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -51,6 +51,13 @@ using epee::string_tools::pod_to_hex;
namespace
{
+struct pre_rct_output_data_t
+{
+ crypto::public_key pubkey; //!< the output's public key (for spend verification)
+ uint64_t unlock_time; //!< the output's unlock time (or height)
+ uint64_t height; //!< the height of the block which created the output
+};
+
template <typename T>
inline void throw0(const T &e)
{
@@ -242,6 +249,12 @@ typedef struct txindex {
tx_data_t data;
} txindex;
+typedef struct pre_rct_outkey {
+ uint64_t amount_index;
+ uint64_t output_id;
+ pre_rct_output_data_t data;
+} pre_rct_outkey;
+
typedef struct outkey {
uint64_t amount_index;
uint64_t output_id;
@@ -765,7 +778,8 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
const tx_out& tx_output,
const uint64_t& local_index,
- const uint64_t unlock_time)
+ const uint64_t unlock_time,
+ const rct::key *commitment)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -778,6 +792,8 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
if (tx_output.target.type() != typeid(txout_to_key))
throw0(DB_ERROR("Wrong output type: expected txout_to_key"));
+ if (tx_output.amount == 0 && !commitment)
+ throw0(DB_ERROR("RCT output without commitment"));
outtx ot = {m_num_outputs, tx_hash, local_index};
MDB_val_set(vot, ot);
@@ -806,8 +822,16 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
ok.data.pubkey = boost::get < txout_to_key > (tx_output.target).key;
ok.data.unlock_time = unlock_time;
ok.data.height = m_height;
+ if (tx_output.amount == 0)
+ {
+ ok.data.commitment = *commitment;
+ data.mv_size = sizeof(ok);
+ }
+ else
+ {
+ data.mv_size = sizeof(pre_rct_outkey);
+ }
data.mv_data = &ok;
- data.mv_size = sizeof(ok);
if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP)))
throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str()));
@@ -1234,6 +1258,7 @@ void BlockchainLMDB::reset()
mdb_txn_safe txn;
if (auto result = mdb_txn_begin(m_env, NULL, 0, txn))
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+
if (auto result = mdb_drop(txn, m_blocks, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_blocks: ", result).c_str()));
if (auto result = mdb_drop(txn, m_block_info, 0))
@@ -1242,6 +1267,8 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_block_heights: ", result).c_str()));
if (auto result = mdb_drop(txn, m_txs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_txs: ", result).c_str()));
+ if (auto result = mdb_drop(txn, m_tx_indices, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_indices: ", result).c_str()));
if (auto result = mdb_drop(txn, m_tx_outputs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_outputs: ", result).c_str()));
if (auto result = mdb_drop(txn, m_output_txs, 0))
@@ -1255,6 +1282,13 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_hf_versions: ", result).c_str()));
if (auto result = mdb_drop(txn, m_properties, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_properties: ", result).c_str()));
+
+ // init with current version
+ MDB_val_copy<const char*> k("version");
+ MDB_val_copy<uint32_t> v(VERSION);
+ if (auto result = mdb_put(txn, m_properties, &k, &v, 0))
+ throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str()));
+
txn.commit();
m_height = 0;
m_num_outputs = 0;
@@ -1923,8 +1957,19 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6
throw1(OUTPUT_DNE("Attempting to get output pubkey by index, but key does not exist"));
else if (get_result)
throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db"));
- outkey *okp = (outkey *)v.mv_data;
- output_data_t ret = okp->data;
+
+ output_data_t ret;
+ if (amount == 0)
+ {
+ const outkey *okp = (const outkey *)v.mv_data;
+ ret = okp->data;
+ }
+ else
+ {
+ const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
+ memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));;
+ ret.commitment = rct::zeroCommit(amount);
+ }
TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2547,8 +2592,18 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui
else if (get_result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str()));
- outkey *okp = (outkey *)v.mv_data;
- output_data_t data = okp->data;
+ output_data_t data;
+ if (amount == 0)
+ {
+ const outkey *okp = (const outkey *)v.mv_data;
+ data = okp->data;
+ }
+ else
+ {
+ const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
+ memcpy(&data, &okp->data, sizeof(pre_rct_output_data_t));
+ data.commitment = rct::zeroCommit(amount);
+ }
outputs.push_back(data);
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index d60701bbe..050e9e0ae 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -30,6 +30,7 @@
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_protocol/blobdatatype.h" // for type blobdata
+#include "ringct/rctTypes.h"
#include <boost/thread/tss.hpp>
#include <lmdb.h>
@@ -292,7 +293,8 @@ private:
virtual uint64_t add_output(const crypto::hash& tx_hash,
const tx_out& tx_output,
const uint64_t& local_index,
- const uint64_t unlock_time
+ const uint64_t unlock_time,
+ const rct::key *commitment
);
virtual void add_tx_amount_output_indices(const uint64_t tx_id,
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index 96b30b83b..d98c2ce83 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -26,15 +26,6 @@
# 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.
-set(blockchain_converter_sources
- blockchain_converter.cpp
- )
-
-set(blockchain_converter_private_headers)
-
-bitmonero_private_headers(blockchain_converter
- ${blockchain_converter_private_headers})
-
set(blockchain_import_sources
blockchain_import.cpp
bootstrap_file.cpp
@@ -87,30 +78,6 @@ bitmonero_private_headers(cn_deserialize
${cn_deserialize_private_headers})
-if (BLOCKCHAIN_DB STREQUAL DB_LMDB)
-bitmonero_add_executable(blockchain_converter
- ${blockchain_converter_sources}
- ${blockchain_converter_private_headers})
-
-target_link_libraries(blockchain_converter
- LINK_PRIVATE
- cryptonote_core
- p2p
- blockchain_db
- ${CMAKE_THREAD_LIBS_INIT})
-
-if(ARCH_WIDTH)
- target_compile_definitions(blockchain_converter
- PUBLIC -DARCH_WIDTH=${ARCH_WIDTH})
-endif()
-
-add_dependencies(blockchain_converter
- version)
-set_property(TARGET blockchain_converter
- PROPERTY
- OUTPUT_NAME "blockchain_converter")
-endif ()
-
bitmonero_add_executable(blockchain_import
${blockchain_import_sources}
${blockchain_import_private_headers})
diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md
index 9c69647ad..7df6937ad 100644
--- a/src/blockchain_utilities/README.md
+++ b/src/blockchain_utilities/README.md
@@ -18,9 +18,10 @@ e.g.
This is also the default compile setting on the master branch.
-By default, the exporter will use the original in-memory database (blockchain.bin) as its source.
-This default is to make migrating to an LMDB database easy, without having to recompile anything.
-To change the source, adjust `SOURCE_DB` in `src/blockchain_utilities/bootstrap_file.h` according to the comments.
+The exporter will use the LMDB database as its source.
+If you are still using an old style in-memory database (blockchain.bin), you will
+have to either resync from scratch, or use an older version of the tools to export
+it and import it.
## Usage:
diff --git a/src/blockchain_utilities/blockchain_converter.cpp b/src/blockchain_utilities/blockchain_converter.cpp
deleted file mode 100644
index 7c33ec399..000000000
--- a/src/blockchain_utilities/blockchain_converter.cpp
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include "include_base_utils.h"
-#include "common/util.h"
-#include "warnings.h"
-#include "crypto/crypto.h"
-#include "cryptonote_config.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "misc_language.h"
-#include "cryptonote_core/blockchain_storage.h"
-#include "blockchain_db/blockchain_db.h"
-#include "cryptonote_core/blockchain.h"
-#include "blockchain_db/lmdb/db_lmdb.h"
-#include "cryptonote_core/tx_pool.h"
-#include "common/command_line.h"
-#include "serialization/json_utils.h"
-#include "include_base_utils.h"
-#include "version.h"
-#include <iostream>
-
-
-namespace
-{
-
-// CONFIG
-bool opt_batch = true;
-bool opt_resume = true;
-bool opt_testnet = false;
-
-// number of blocks per batch transaction
-// adjustable through command-line argument according to available RAM
-#if ARCH_WIDTH != 32
-uint64_t db_batch_size_verify = 5000;
-#else
-// set a lower default batch size for Windows, pending possible LMDB issue with
-// large batch size.
-uint64_t db_batch_size_verify = 100;
-#endif
-
-// converter only uses verify mode
-uint64_t db_batch_size = db_batch_size_verify;
-
-}
-
-namespace po = boost::program_options;
-
-using namespace cryptonote;
-using namespace epee;
-
-struct fake_core
-{
- Blockchain dummy;
- tx_memory_pool m_pool;
-
- blockchain_storage m_storage;
-
-#if !defined(BLOCKCHAIN_DB)
- // for multi_db_runtime:
- fake_core(const boost::filesystem::path &path, const bool use_testnet) : dummy(m_pool), m_pool(&dummy), m_storage(m_pool)
-#else
- // for multi_db_compile:
- fake_core(const boost::filesystem::path &path, const bool use_testnet) : dummy(m_pool), m_pool(dummy), m_storage(&m_pool)
-#endif
- {
- m_pool.init(path.string());
- m_storage.init(path.string(), use_testnet);
- }
-};
-
-int main(int argc, char* argv[])
-{
- uint64_t height = 0;
- uint64_t start_block = 0;
- uint64_t end_block = 0;
- uint64_t num_blocks = 0;
-
- boost::filesystem::path default_data_path {tools::get_default_data_dir()};
- boost::filesystem::path default_testnet_data_path {default_data_path / "testnet"};
-
-
- po::options_description desc_cmd_only("Command line options");
- po::options_description desc_cmd_sett("Command line options and settings options");
- const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", LOG_LEVEL_0};
- const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size};
- const command_line::arg_descriptor<bool> arg_testnet_on = {
- "testnet"
- , "Run on testnet."
- , opt_testnet
- };
- const command_line::arg_descriptor<uint64_t> arg_block_number =
- {"block-number", "Number of blocks (default: use entire source blockchain)",
- 0};
-
- command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string());
- command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string());
- command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_batch_size);
- command_line::add_arg(desc_cmd_sett, arg_testnet_on);
- command_line::add_arg(desc_cmd_sett, arg_block_number);
-
- command_line::add_arg(desc_cmd_only, command_line::arg_help);
-
- const command_line::arg_descriptor<bool> arg_batch = {"batch",
- "Batch transactions for faster import", true};
- const command_line::arg_descriptor<bool> arg_resume = {"resume",
- "Resume from current height if output database already exists", true};
-
- // call add_options() directly for these arguments since command_line helpers
- // support only boolean switch, not boolean argument
- desc_cmd_sett.add_options()
- (arg_batch.name, make_semantic(arg_batch), arg_batch.description)
- (arg_resume.name, make_semantic(arg_resume), arg_resume.description)
- ;
-
- po::options_description desc_options("Allowed options");
- desc_options.add(desc_cmd_only).add(desc_cmd_sett);
-
-
- po::variables_map vm;
- bool r = command_line::handle_error_helper(desc_options, [&]()
- {
- po::store(po::parse_command_line(argc, argv, desc_options), vm);
- po::notify(vm);
-
- return true;
- });
- if (!r)
- return 1;
-
- int log_level = command_line::get_arg(vm, arg_log_level);
- opt_batch = command_line::get_arg(vm, arg_batch);
- opt_resume = command_line::get_arg(vm, arg_resume);
- db_batch_size = command_line::get_arg(vm, arg_batch_size);
-
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
- std::cout << desc_options << std::endl;
- return 1;
- }
-
- if (! opt_batch && ! vm["batch-size"].defaulted())
- {
- std::cerr << "Error: batch-size set, but batch option not enabled" << ENDL;
- return 1;
- }
- if (! db_batch_size)
- {
- std::cerr << "Error: batch-size must be > 0" << ENDL;
- return 1;
- }
-
- log_space::get_set_log_detalisation_level(true, log_level);
- log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
- LOG_PRINT_L0("Starting...");
-
- std::string src_folder;
- opt_testnet = command_line::get_arg(vm, arg_testnet_on);
- auto data_dir_arg = opt_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
- src_folder = command_line::get_arg(vm, data_dir_arg);
- boost::filesystem::path dest_folder(src_folder);
-
- num_blocks = command_line::get_arg(vm, arg_block_number);
-
- if (opt_batch)
- {
- LOG_PRINT_L0("batch: " << std::boolalpha << opt_batch << std::noboolalpha
- << " batch size: " << db_batch_size);
- }
- else
- {
- LOG_PRINT_L0("batch: " << std::boolalpha << opt_batch << std::noboolalpha);
- }
- LOG_PRINT_L0("resume: " << std::boolalpha << opt_resume << std::noboolalpha);
- LOG_PRINT_L0("testnet: " << std::boolalpha << opt_testnet << std::noboolalpha);
-
- fake_core c(src_folder, opt_testnet);
-
- height = c.m_storage.get_current_blockchain_height();
-
- BlockchainDB *blockchain;
- blockchain = new BlockchainLMDB(opt_batch);
- dest_folder /= blockchain->get_db_name();
- LOG_PRINT_L0("Source blockchain: " << src_folder);
- LOG_PRINT_L0("Dest blockchain: " << dest_folder.string());
- LOG_PRINT_L0("Opening dest blockchain (BlockchainDB " << blockchain->get_db_name() << ")");
- blockchain->open(dest_folder.string());
- LOG_PRINT_L0("Source blockchain height: " << height);
- LOG_PRINT_L0("Dest blockchain height: " << blockchain->height());
-
- if (opt_resume)
- // next block number to add is same as current height
- start_block = blockchain->height();
-
- if (! num_blocks || (start_block + num_blocks > height))
- end_block = height - 1;
- else
- end_block = start_block + num_blocks - 1;
-
- LOG_PRINT_L0("start height: " << start_block+1 << " stop height: " <<
- end_block+1);
-
- if (start_block > end_block)
- {
- LOG_PRINT_L0("Finished: no blocks to add");
- delete blockchain;
- return 0;
- }
-
- if (opt_batch)
- blockchain->batch_start(db_batch_size);
- uint64_t i = 0;
- for (i = start_block; i < end_block + 1; ++i)
- {
- // block: i height: i+1 end height: end_block + 1
- if ((i+1) % 10 == 0)
- {
- std::cout << "\r \r" << "height " << i+1 << "/" <<
- end_block+1 << " (" << (i+1)*100/(end_block+1)<< "%)" << std::flush;
- }
- // for debugging:
- // std::cout << "height " << i+1 << "/" << end_block+1
- // << " ((" << i+1 << ")*100/(end_block+1))" << "%)" << ENDL;
-
- block b = c.m_storage.get_block(i);
- size_t bsize = c.m_storage.get_block_size(i);
- difficulty_type bdiff = c.m_storage.get_block_cumulative_difficulty(i);
- uint64_t bcoins = c.m_storage.get_block_coins_generated(i);
- std::vector<transaction> txs;
- std::vector<crypto::hash> missed;
-
- c.m_storage.get_transactions(b.tx_hashes, txs, missed);
- if (missed.size())
- {
- std::cout << ENDL;
- std::cerr << "Missed transaction(s) for block at height " << i + 1 << ", exiting" << ENDL;
- delete blockchain;
- return 1;
- }
-
- try
- {
- blockchain->add_block(b, bsize, bdiff, bcoins, txs);
-
- if (opt_batch)
- {
- if ((i < end_block) && ((i + 1) % db_batch_size == 0))
- {
- std::cout << "\r \r";
- std::cout << "[- batch commit at height " << i + 1 << " -]" << ENDL;
- blockchain->batch_stop();
- blockchain->batch_start(db_batch_size);
- std::cout << ENDL;
- blockchain->show_stats();
- }
- }
- }
- catch (const std::exception& e)
- {
- std::cout << ENDL;
- std::cerr << "Error adding block " << i << " to new blockchain: " << e.what() << ENDL;
- delete blockchain;
- return 2;
- }
- }
- if (opt_batch)
- {
- std::cout << "\r \r" << "height " << i << "/" <<
- end_block+1 << " (" << (i)*100/(end_block+1)<< "%)" << std::flush;
- std::cout << ENDL;
- std::cout << "[- batch commit at height " << i << " -]" << ENDL;
- blockchain->batch_stop();
- }
- std::cout << ENDL;
- blockchain->show_stats();
- std::cout << "Finished at height: " << i << " block: " << i-1 << ENDL;
-
- delete blockchain;
- return 0;
-}
diff --git a/src/blockchain_utilities/blockchain_dump.cpp b/src/blockchain_utilities/blockchain_dump.cpp
index 23619eaeb..626900d42 100644
--- a/src/blockchain_utilities/blockchain_dump.cpp
+++ b/src/blockchain_utilities/blockchain_dump.cpp
@@ -27,8 +27,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/blockchain_storage.h"
#include "cryptonote_core/blockchain.h"
+#include "cryptonote_core/tx_pool.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#ifdef BERKELEY_DB
@@ -103,7 +103,6 @@ int main(int argc, char* argv[])
const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level};
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
-#if SOURCE_DB != DB_MEMORY
const command_line::arg_descriptor<std::string> arg_db_type = {
"db-type"
, "Specify database type"
@@ -114,7 +113,6 @@ int main(int argc, char* argv[])
, "Include data that is only in a database version."
, false
};
-#endif
const command_line::arg_descriptor<bool> arg_testnet_on = {
"testnet"
, "Run on testnet."
@@ -125,10 +123,8 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, command_line::arg_data_dir, default_data_path.string());
command_line::add_arg(desc_cmd_sett, command_line::arg_testnet_data_dir, default_testnet_data_path.string());
command_line::add_arg(desc_cmd_sett, arg_output_file);
-#if SOURCE_DB != DB_MEMORY
command_line::add_arg(desc_cmd_sett, arg_db_type);
command_line::add_arg(desc_cmd_sett, arg_include_db_only_data);
-#endif
command_line::add_arg(desc_cmd_sett, arg_testnet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
@@ -164,9 +160,7 @@ int main(int argc, char* argv[])
LOG_PRINT_L0("Setting log level = " << log_level);
bool opt_testnet = command_line::get_arg(vm, arg_testnet_on);
-#if SOURCE_DB != DB_MEMORY
bool opt_include_db_only_data = command_line::get_arg(vm, arg_include_db_only_data);
-#endif
std::string m_config_folder;
@@ -208,15 +202,6 @@ int main(int argc, char* argv[])
// If we wanted to use the memory pool, we would set up a fake_core.
-#if SOURCE_DB == DB_MEMORY
- // blockchain_storage* core_storage = NULL;
- // tx_memory_pool m_mempool(*core_storage); // is this fake anyway? just passing in NULL! so m_mempool can't be used anyway, right?
- // core_storage = new blockchain_storage(&m_mempool);
-
- blockchain_storage* core_storage = new blockchain_storage(NULL);
- LOG_PRINT_L0("Initializing source blockchain (in-memory database)");
- r = core_storage->init(m_config_folder, opt_testnet);
-#else
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
// 1. Blockchain has the init() method for easy setup
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
@@ -267,7 +252,6 @@ int main(int argc, char* argv[])
return 1;
}
r = core_storage->init(db, opt_testnet);
-#endif
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
@@ -340,7 +324,6 @@ int main(int argc, char* argv[])
write_pod(d,key_images[n]);
}
end_compound(d);
-#if SOURCE_DB != DB_MEMORY
if (opt_include_db_only_data)
{
start_struct(d, "block_timestamps", true);
@@ -420,7 +403,6 @@ int main(int argc, char* argv[])
write_pod(d, boost::lexical_cast<std::string>(h), (unsigned int)db->get_hard_fork_version(h));
end_compound(d);
}
-#endif
end_compound(d);
CHECK_AND_ASSERT_MES(r, false, "Failed to dump blockchain");
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 964c610cd..231bea337 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -29,6 +29,7 @@
#include "bootstrap_file.h"
#include "blocksdat_file.h"
#include "common/command_line.h"
+#include "cryptonote_core/tx_pool.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#if defined(BERKELEY_DB)
@@ -53,11 +54,7 @@ std::string join_set_strings(const std::unordered_set<std::string>& db_types_all
int main(int argc, char* argv[])
{
-#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY)
- std::string default_db_type = "memory";
-#else
std::string default_db_type = "lmdb";
-#endif
std::unordered_set<std::string> db_types_all = cryptonote::blockchain_db_types;
db_types_all.insert("memory");
@@ -160,15 +157,6 @@ int main(int argc, char* argv[])
// If we wanted to use the memory pool, we would set up a fake_core.
-#if SOURCE_DB == DB_MEMORY
- // blockchain_storage* core_storage = NULL;
- // tx_memory_pool m_mempool(*core_storage); // is this fake anyway? just passing in NULL! so m_mempool can't be used anyway, right?
- // core_storage = new blockchain_storage(&m_mempool);
-
- blockchain_storage* core_storage = new blockchain_storage(NULL);
- LOG_PRINT_L0("Initializing source blockchain (in-memory database)");
- r = core_storage->init(m_config_folder, opt_testnet);
-#else
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
// 1. Blockchain has the init() method for easy setup
// 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
@@ -217,7 +205,6 @@ int main(int argc, char* argv[])
return 1;
}
r = core_storage->init(db, opt_testnet);
-#endif
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize source blockchain storage");
LOG_PRINT_L0("Source blockchain storage initialized OK");
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 1aaf2bddc..27c9050f8 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -222,12 +222,8 @@ int pop_blocks(FakeCore& simple_core, int num_blocks)
std::vector<transaction> popped_txs;
for (int i=0; i < num_blocks; ++i)
{
-#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY)
- simple_core.m_storage.debug_pop_block_from_blockchain();
-#else
// simple_core.m_storage.pop_block_from_blockchain() is private, so call directly through db
simple_core.m_storage.get_db().pop_block(popped_block, popped_txs);
-#endif
quit = 1;
}
@@ -244,9 +240,7 @@ int pop_blocks(FakeCore& simple_core, int num_blocks)
{
simple_core.batch_stop();
}
-#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB)
simple_core.m_storage.get_db().show_stats();
-#endif
}
return num_blocks;
@@ -255,11 +249,6 @@ int pop_blocks(FakeCore& simple_core, int num_blocks)
template <typename FakeCore>
int import_from_file(FakeCore& simple_core, const std::string& import_file_path, uint64_t block_stop=0)
{
-#if !defined(BLOCKCHAIN_DB)
- static_assert(std::is_same<fake_core_memory, FakeCore>::value || std::is_same<fake_core_db, FakeCore>::value,
- "FakeCore constraint error");
-#endif
-#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB)
if (std::is_same<fake_core_db, FakeCore>::value)
{
// Reset stats, in case we're using newly created db, accumulating stats
@@ -267,7 +256,6 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
// This aligns internal db counts with importer counts.
simple_core.m_storage.get_db().reset_stats();
}
-#endif
boost::filesystem::path fs_import_file_path(import_file_path);
boost::system::error_code ec;
if (!boost::filesystem::exists(fs_import_file_path, ec))
@@ -567,9 +555,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
simple_core.batch_stop();
simple_core.batch_start(db_batch_size);
std::cout << ENDL;
-#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB)
simple_core.m_storage.get_db().show_stats();
-#endif
}
}
}
@@ -595,9 +581,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
{
simple_core.batch_stop();
}
-#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB)
simple_core.m_storage.get_db().show_stats();
-#endif
LOG_PRINT_L0("Number of blocks imported: " << num_imported);
if (h > 0)
// TODO: if there was an error, the last added block is probably at zero-based height h-2
@@ -609,13 +593,8 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
int main(int argc, char* argv[])
{
-#if defined(BLOCKCHAIN_DB) && (BLOCKCHAIN_DB == DB_MEMORY)
- std::string default_db_type = "memory";
- std::string default_db_engine_compiled = "memory";
-#else
std::string default_db_type = "lmdb";
std::string default_db_engine_compiled = "blockchain_db";
-#endif
std::unordered_set<std::string> db_types_all = cryptonote::blockchain_db_types;
db_types_all.insert("memory");
@@ -817,17 +796,11 @@ int main(int argc, char* argv[])
// circumstance.
// for multi_db_runtime:
-#if !defined(BLOCKCHAIN_DB)
if (db_type == "lmdb" || db_type == "berkeley")
{
fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags);
import_from_file(simple_core, import_file_path, block_stop);
}
- else if (db_type == "memory")
- {
- fake_core_memory simple_core(m_config_folder, opt_testnet);
- import_from_file(simple_core, import_file_path, block_stop);
- }
else
{
std::cerr << "database type unrecognized" << ENDL;
@@ -835,17 +808,7 @@ int main(int argc, char* argv[])
}
// for multi_db_compile:
-#else
- if (db_engine_compiled != default_db_engine_compiled)
- {
- std::cerr << "Invalid database type for compiled version: " << db_type << std::endl;
- return 1;
- }
-#if BLOCKCHAIN_DB == DB_LMDB
fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags);
-#else
- fake_core_memory simple_core(m_config_folder, opt_testnet);
-#endif
if (! vm["pop-blocks"].defaulted())
{
@@ -856,17 +819,14 @@ int main(int argc, char* argv[])
return 0;
}
-#if !defined(BLOCKCHAIN_DB) || (BLOCKCHAIN_DB == DB_LMDB)
if (! vm["drop-hard-fork"].defaulted())
{
LOG_PRINT_L0("Dropping hard fork tables...");
simple_core.m_storage.get_db().drop_hard_fork_info();
return 0;
}
-#endif
import_from_file(simple_core, import_file_path, block_stop);
-#endif
}
catch (const DB_ERROR& e)
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index 51c17a9b6..6ddda65ec 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -31,21 +31,6 @@
#include "version.h"
-// CONFIG: choose one of the three #define's
-//
-// DB_MEMORY was a sensible default for users migrating to LMDB, as it allowed
-// the exporter to use the in-memory blockchain while the other binaries
-// work with LMDB, without recompiling anything.
-//
-// Now that the majority of users are on 0.9.2, and the converter has largely
-// become a generalised database tool, the default has changed to DB_LMDB.
-//
-// #define SOURCE_DB DB_MEMORY
-#define SOURCE_DB DB_LMDB
-// to use global compile-time setting (DB_MEMORY or DB_LMDB):
-// #define SOURCE_DB BLOCKCHAIN_DB
-
-
// bounds checking is done before writing to buffer, but buffer size
// should be a sensible maximum
#define BUFFER_SIZE 1000000
diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp
index 81b56d55e..926562ba2 100644
--- a/src/blockchain_utilities/blocksdat_file.cpp
+++ b/src/blockchain_utilities/blocksdat_file.cpp
@@ -114,11 +114,7 @@ bool BlocksdatFile::close()
}
-#if SOURCE_DB == DB_MEMORY
-bool BlocksdatFile::store_blockchain_raw(blockchain_storage* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop)
-#else
bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop)
-#endif
{
uint64_t num_blocks_written = 0;
m_blockchain_storage = _blockchain_storage;
diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h
index 92bae9fcb..ed821cd6d 100644
--- a/src/blockchain_utilities/blocksdat_file.h
+++ b/src/blockchain_utilities/blocksdat_file.h
@@ -36,7 +36,6 @@
#include "cryptonote_core/cryptonote_basic.h"
#include "cryptonote_core/cryptonote_boost_serialization.h"
-#include "cryptonote_core/blockchain_storage.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
@@ -60,21 +59,12 @@ class BlocksdatFile
{
public:
-#if SOURCE_DB == DB_MEMORY
- bool store_blockchain_raw(cryptonote::blockchain_storage* cs, cryptonote::tx_memory_pool* txp,
- boost::filesystem::path& output_file, uint64_t use_block_height=0);
-#else
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
boost::filesystem::path& output_file, uint64_t use_block_height=0);
-#endif
protected:
-#if SOURCE_DB == DB_MEMORY
- blockchain_storage* m_blockchain_storage;
-#else
Blockchain* m_blockchain_storage;
-#endif
std::ofstream * m_raw_data_file;
diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp
index da3b44593..61bd35a6f 100644
--- a/src/blockchain_utilities/bootstrap_file.cpp
+++ b/src/blockchain_utilities/bootstrap_file.cpp
@@ -223,31 +223,9 @@ void BootstrapFile::write_block(block& block)
{
throw std::runtime_error("Aborting: tx == null_hash");
}
-#if SOURCE_DB == DB_MEMORY
- const transaction* tx = m_blockchain_storage->get_tx(tx_id);
-#else
transaction tx = m_blockchain_storage->get_db().get_tx(tx_id);
-#endif
-#if SOURCE_DB == DB_MEMORY
- if(tx == NULL)
- {
- if (! m_tx_pool)
- throw std::runtime_error("Aborting: tx == NULL, so memory pool required to get tx, but memory pool isn't enabled");
- else
- {
- transaction tx;
- if(m_tx_pool->get_transaction(tx_id, tx))
- txs.push_back(tx);
- else
- throw std::runtime_error("Aborting: tx not found in pool");
- }
- }
- else
- txs.push_back(*tx);
-#else
txs.push_back(tx);
-#endif
}
// these non-coinbase txs will be serialized using this structure
@@ -257,15 +235,9 @@ void BootstrapFile::write_block(block& block)
bool include_extra_block_data = true;
if (include_extra_block_data)
{
-#if SOURCE_DB == DB_MEMORY
- size_t block_size = m_blockchain_storage->get_block_size(block_height);
- difficulty_type cumulative_difficulty = m_blockchain_storage->get_block_cumulative_difficulty(block_height);
- uint64_t coins_generated = m_blockchain_storage->get_block_coins_generated(block_height);
-#else
size_t block_size = m_blockchain_storage->get_db().get_block_size(block_height);
difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height);
uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height);
-#endif
bp.block_size = block_size;
bp.cumulative_difficulty = cumulative_difficulty;
@@ -288,11 +260,7 @@ bool BootstrapFile::close()
}
-#if SOURCE_DB == DB_MEMORY
-bool BootstrapFile::store_blockchain_raw(blockchain_storage* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop)
-#else
bool BootstrapFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_memory_pool* _tx_pool, boost::filesystem::path& output_file, uint64_t requested_block_stop)
-#endif
{
uint64_t num_blocks_written = 0;
m_max_chunk = 0;
diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h
index dd134f7ae..0bbe28f17 100644
--- a/src/blockchain_utilities/bootstrap_file.h
+++ b/src/blockchain_utilities/bootstrap_file.h
@@ -35,7 +35,6 @@
#include <boost/iostreams/filtering_streambuf.hpp>
#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/blockchain_storage.h"
#include "cryptonote_core/blockchain.h"
#include <algorithm>
@@ -60,21 +59,12 @@ public:
uint64_t count_blocks(const std::string& dir_path);
uint64_t seek_to_first_chunk(std::ifstream& import_file);
-#if SOURCE_DB == DB_MEMORY
- bool store_blockchain_raw(cryptonote::blockchain_storage* cs, cryptonote::tx_memory_pool* txp,
- boost::filesystem::path& output_file, uint64_t use_block_height=0);
-#else
bool store_blockchain_raw(cryptonote::Blockchain* cs, cryptonote::tx_memory_pool* txp,
boost::filesystem::path& output_file, uint64_t use_block_height=0);
-#endif
protected:
-#if SOURCE_DB == DB_MEMORY
- blockchain_storage* m_blockchain_storage;
-#else
Blockchain* m_blockchain_storage;
-#endif
tx_memory_pool* m_tx_pool;
typedef std::vector<char> buffer_type;
diff --git a/src/blockchain_utilities/fake_core.h b/src/blockchain_utilities/fake_core.h
index 2fb5031d3..ba1c2ed72 100644
--- a/src/blockchain_utilities/fake_core.h
+++ b/src/blockchain_utilities/fake_core.h
@@ -30,7 +30,7 @@
#include <boost/filesystem.hpp>
#include "cryptonote_core/blockchain.h" // BlockchainDB
-#include "cryptonote_core/blockchain_storage.h" // in-memory DB
+#include "cryptonote_core/tx_pool.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#if defined(BERKELEY_DB)
@@ -48,8 +48,6 @@ namespace
}
-#if !defined(BLOCKCHAIN_DB) || BLOCKCHAIN_DB == DB_LMDB
-
struct fake_core_db
{
Blockchain m_storage;
@@ -59,12 +57,7 @@ struct fake_core_db
bool support_add_block;
// for multi_db_runtime:
-#if !defined(BLOCKCHAIN_DB)
- fake_core_db(const boost::filesystem::path &path, const bool use_testnet=false, const bool do_batch=true, const std::string& db_type="lmdb", const int db_flags=0) : m_pool(&m_storage), m_storage(m_pool)
- // for multi_db_compile:
-#else
fake_core_db(const boost::filesystem::path &path, const bool use_testnet=false, const bool do_batch=true, const std::string& db_type="lmdb", const int db_flags=0) : m_pool(m_storage), m_storage(m_pool)
-#endif
{
m_pool.init(path.string());
@@ -137,59 +130,4 @@ struct fake_core_db
}
};
-#endif
-
-#if !defined(BLOCKCHAIN_DB) || BLOCKCHAIN_DB == DB_MEMORY
-
-struct fake_core_memory
-{
- blockchain_storage m_storage;
- tx_memory_pool m_pool;
- bool support_batch;
- bool support_add_block;
-
- // for multi_db_runtime:
-#if !defined(BLOCKCHAIN_DB)
- fake_core_memory(const boost::filesystem::path &path, const bool use_testnet=false) : m_pool(&m_storage), m_storage(m_pool)
-#else
- // for multi_db_compile:
- fake_core_memory(const boost::filesystem::path &path, const bool use_testnet=false) : m_pool(m_storage), m_storage(&m_pool)
-#endif
- {
- m_pool.init(path.string());
- m_storage.init(path.string(), use_testnet);
- support_batch = false;
- support_add_block = false;
- }
- ~fake_core_memory()
- {
- LOG_PRINT_L3("fake_core_memory() destructor called - want to see it ripple down");
- m_storage.deinit();
- }
-
- uint64_t add_block(const block& blk
- , const size_t& block_size
- , const difficulty_type& cumulative_difficulty
- , const uint64_t& coins_generated
- , const std::vector<transaction>& txs
- )
- {
- // TODO:
- // would need to refactor handle_block_to_main_chain() to have a direct add_block() method like Blockchain class
- throw std::runtime_error("direct add_block() method not implemented for in-memory db");
- return 2;
- }
-
- void batch_start(uint64_t batch_num_blocks = 0)
- {
- LOG_PRINT_L0("WARNING: [batch_start] opt_batch set, but this database doesn't support/need transactions - ignoring");
- }
-
- void batch_stop()
- {
- LOG_PRINT_L0("WARNING: [batch_stop] opt_batch set, but this database doesn't support/need transactions - ignoring");
- }
-
-};
-#endif
diff --git a/src/common/json_util.h b/src/common/json_util.h
index 6f8b4c18f..45046a4fb 100644
--- a/src/common/json_util.h
+++ b/src/common/json_util.h
@@ -28,8 +28,8 @@
#pragma once
-#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory) \
- type field_##name; \
+#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory, def) \
+ type field_##name = def; \
bool field_##name##_found = false; \
(void)field_##name##_found; \
do if (json.HasMember(#name)) \
diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c
index a9b659a6b..1b390e402 100644
--- a/src/crypto/crypto-ops.c
+++ b/src/crypto/crypto-ops.c
@@ -40,17 +40,15 @@ DISABLE_VS_WARNINGS(4146 4244)
static void fe_mul(fe, const fe, const fe);
static void fe_sq(fe, const fe);
-static void fe_tobytes(unsigned char *, const fe);
static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_p2_0(ge_p2 *);
static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *);
-static void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
static void fe_divpowm1(fe, const fe, const fe);
/* Common functions */
-static uint64_t load_3(const unsigned char *in) {
+uint64_t load_3(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
@@ -58,7 +56,7 @@ static uint64_t load_3(const unsigned char *in) {
return result;
}
-static uint64_t load_4(const unsigned char *in)
+uint64_t load_4(const unsigned char *in)
{
uint64_t result;
result = (uint64_t) in[0];
@@ -120,7 +118,7 @@ Postconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
-static void fe_add(fe h, const fe f, const fe g) {
+void fe_add(fe h, const fe f, const fe g) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@@ -258,7 +256,7 @@ static void fe_copy(fe h, const fe f) {
/* From fe_invert.c */
-static void fe_invert(fe out, const fe z) {
+void fe_invert(fe out, const fe z) {
fe t0;
fe t1;
fe t2;
@@ -1031,7 +1029,7 @@ Proof:
so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
*/
-static void fe_tobytes(unsigned char *s, const fe h) {
+void fe_tobytes(unsigned char *s, const fe h) {
int32_t h0 = h[0];
int32_t h1 = h[1];
int32_t h2 = h[2];
@@ -1591,7 +1589,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
r = p - q
*/
-static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
+void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h
index cdc5ac1ee..4986499f4 100644
--- a/src/crypto/crypto-ops.h
+++ b/src/crypto/crypto-ops.h
@@ -143,3 +143,11 @@ void sc_sub(unsigned char *, const unsigned char *, const unsigned char *);
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *);
int sc_check(const unsigned char *);
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
+
+// internal
+uint64_t load_3(const unsigned char *in);
+uint64_t load_4(const unsigned char *in);
+void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
+void fe_add(fe h, const fe f, const fe g);
+void fe_tobytes(unsigned char *, const fe);
+void fe_invert(fe out, const fe z);
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index f5e655274..250779ac3 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -151,7 +151,7 @@ namespace crypto {
return true;
}
- static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) {
+ void crypto_ops::derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) {
struct {
key_derivation derivation;
char output_index[(sizeof(size_t) * 8 + 6) / 7];
@@ -230,7 +230,7 @@ namespace crypto {
buf.h = prefix_hash;
buf.key = pub;
if (ge_frombytes_vartime(&tmp3, &pub) != 0) {
- abort();
+ return false;
}
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) {
return false;
@@ -364,7 +364,7 @@ POP_WARNINGS
return false;
}
if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) {
- abort();
+ return false;
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index fa55c2aab..b396fc7db 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -64,6 +64,22 @@ namespace crypto {
friend class crypto_ops;
};
+ POD_CLASS public_keyV {
+ std::vector<public_key> keys;
+ int rows;
+ };
+
+ POD_CLASS secret_keyV {
+ std::vector<secret_key> keys;
+ int rows;
+ };
+
+ POD_CLASS public_keyM {
+ int cols;
+ int rows;
+ std::vector<secret_keyV> column_vectors;
+ };
+
POD_CLASS key_derivation: ec_point {
friend class crypto_ops;
};
@@ -97,6 +113,8 @@ namespace crypto {
friend bool secret_key_to_public_key(const secret_key &, public_key &);
static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &);
friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &);
+ static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res);
+ friend void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res);
static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
@@ -165,6 +183,9 @@ namespace crypto {
const public_key &base, public_key &derived_key) {
return crypto_ops::derive_public_key(derivation, output_index, base, derived_key);
}
+ inline void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) {
+ return crypto_ops::derivation_to_scalar(derivation, output_index, res);
+ }
inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index,
const secret_key &base, secret_key &derived_key) {
crypto_ops::derive_secret_key(derivation, output_index, base, derived_key);
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index 3ee2a887c..090d563a2 100644
--- a/src/crypto/keccak.c
+++ b/src/crypto/keccak.c
@@ -73,11 +73,11 @@ void keccakf(uint64_t st[25], int rounds)
// compute a keccak hash (md) of given byte length from "in"
typedef uint64_t state_t[25];
-int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen)
+int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
{
state_t st;
uint8_t temp[144];
- int i, rsiz, rsizw;
+ size_t i, rsiz, rsizw;
rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen;
rsizw = rsiz / 8;
@@ -106,7 +106,7 @@ int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen)
return 0;
}
-void keccak1600(const uint8_t *in, int inlen, uint8_t *md)
+void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md)
{
keccak(in, inlen, md, sizeof(state_t));
}
diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h
index 4f7f85729..fbd8e1904 100644
--- a/src/crypto/keccak.h
+++ b/src/crypto/keccak.h
@@ -16,11 +16,11 @@
#endif
// compute a keccak hash (md) of given byte length from "in"
-int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen);
+int keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen);
// update the state
void keccakf(uint64_t st[25], int norounds);
-void keccak1600(const uint8_t *in, int inlen, uint8_t *md);
+void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md);
#endif
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 1a093b501..50f61e346 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -41,7 +41,7 @@
#define CRYPTONOTE_MAX_TX_SIZE 1000000000
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
-#define CURRENT_TRANSACTION_VERSION 1
+#define CURRENT_TRANSACTION_VERSION 2
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 0
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 205356797..4ebfd01e3 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -28,7 +28,6 @@
set(cryptonote_core_sources
account.cpp
- blockchain_storage.cpp
blockchain.cpp
checkpoints.cpp
cryptonote_basic_impl.cpp
@@ -44,7 +43,6 @@ set(cryptonote_core_headers)
set(cryptonote_core_private_headers
account.h
account_boost_serialization.h
- blockchain_storage.h
blockchain_storage_boost_serialization.h
blockchain.h
checkpoints.h
@@ -80,6 +78,7 @@ target_link_libraries(cryptonote_core
crypto
otshell_utils
blockchain_db
+ ringct
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index cc6b48b6b..ef72dac59 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -51,6 +51,7 @@
#include "crypto/hash.h"
#include "cryptonote_core/checkpoints.h"
#include "cryptonote_core/cryptonote_core.h"
+#include "ringct/rctSigs.h"
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
#endif
@@ -98,6 +99,10 @@ static const struct {
// version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
{ 2, 624634, 0, 1445355000 },
+
+ { 3, 800500, 0, 1472415034 },
+ { 4, 801220, 0, 1472415035 },
+ { 5, 802660, 0, 1472415036 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -127,7 +132,7 @@ bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
// and collects the public key for each from the transaction it was included in
// via the visitor passed to it.
template <class visitor_t>
-bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
+bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -207,7 +212,7 @@ bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, vi
output_index = m_db->get_output_key(tx_in_to_key.amount, i);
// call to the passed boost visitor to grab the public key for the output
- if (!vis.handle_output(output_index.unlock_time, output_index.pubkey))
+ if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, output_index.commitment))
{
LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
return false;
@@ -958,7 +963,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
money_in_use += o.amount;
partial_block_reward = false;
- if (version >= 3) {
+ if (version == 3) {
for (auto &o: b.miner_tx.vout) {
if (!is_valid_decomposed_amount(o.amount)) {
LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
@@ -969,7 +974,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
std::vector<size_t> last_blocks_sizes;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, get_current_hard_fork_version()))
+ if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version))
{
LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
return false;
@@ -1086,14 +1091,24 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
{
LOG_ERROR("Creating block template: error: invalid transaction size");
}
- uint64_t inputs_amount;
- if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
+ if (cur_tx.tx.version == 1)
{
- LOG_ERROR("Creating block template: error: cannot get inputs amount");
+ uint64_t inputs_amount;
+ if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
+ {
+ LOG_ERROR("Creating block template: error: cannot get inputs amount");
+ }
+ else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
+ {
+ LOG_ERROR("Creating block template: error: invalid fee");
+ }
}
- else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
+ else
{
- LOG_ERROR("Creating block template: error: invalid fee");
+ if (cur_tx.fee != cur_tx.tx.txnFee)
+ {
+ LOG_ERROR("Creating block template: error: invalid fee");
+ }
}
}
if (txs_size != real_txs_size)
@@ -1117,7 +1132,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
- bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
+ uint8_t hf_version = m_hardfork->get_current_version();
+ size_t max_outs = hf_version >= 4 ? 1 : 11;
+ bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@@ -1126,7 +1143,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
#endif
for (size_t try_count = 0; try_count != 10; ++try_count)
{
- r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
+ r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
@@ -1597,6 +1614,139 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
return true;
}
//------------------------------------------------------------------
+// This function adds the ringct output at index i to the list
+// unlocked and other such checks should be done by here.
+void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry());
+ oen.amount = amount;
+ oen.global_amount_index = i;
+ output_data_t data = m_db->get_output_key(amount, i);
+ oen.out_key = data.pubkey;
+ oen.commitment = data.commitment;
+}
+//------------------------------------------------------------------
+// This function takes an RPC request for mixins and creates an RPC response
+// with the requested mixins.
+// TODO: figure out why this returns boolean / if we should be returning false
+// in some cases
+bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ // for each amount that we need to get mixins for, get <n> random outputs
+ // from BlockchainDB where <n> is req.outs_count (number of mixins).
+ auto num_outs = m_db->get_num_outputs(0);
+ // ensure we don't include outputs that aren't yet eligible to be used
+ // outpouts are sorted by height
+ while (num_outs > 0)
+ {
+ const tx_out_index toi = m_db->get_output_tx_and_index(0, num_outs - 1);
+ const uint64_t height = m_db->get_tx_block_height(toi.first);
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ break;
+ --num_outs;
+ }
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_outs <= req.outs_count)
+ {
+ for (uint64_t i = 0; i < num_outs; i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(0, i);
+
+ // if tx is unlocked, add output to result_outs
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_rct_random_outs(res.outs, 0, i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (res.outs.size() < req.outs_count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
+ uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
+ double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
+ uint64_t i = (uint64_t)(frac*num_outs);
+ // just in case rounding up to 1 occurs after sqrt
+ if (i == num_outs)
+ --i;
+
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(0, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ add_out_to_get_rct_random_outs(res.outs, 0, i);
+ }
+ }
+ }
+
+ if (res.outs.size() < req.outs_count)
+ return false;
+#if 0
+ // if we do not have enough RCT inputs, we can pick from the non RCT ones
+ // which will have a zero mask
+ if (res.outs.size() < req.outs_count)
+ {
+ LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones");
+
+ // TODO: arbitrary selection, needs better
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2);
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2);
+ req2.outs_count = req.outs_count - res.outs.size();
+ static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000};
+ for (uint64_t a: amounts)
+ req2.amounts.push_back(a);
+ if (!get_random_outs_for_amounts(req2, res2))
+ return false;
+
+ // pick random ones from there
+ while (res.outs.size() < req.outs_count)
+ {
+ int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0]));
+ if (!res2.outs[list_idx].outs.empty())
+ {
+ const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back();
+ res2.outs[list_idx].outs.pop_back();
+ add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index);
+ }
+ }
+ }
+#endif
+
+ return true;
+}
+//------------------------------------------------------------------
bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1607,11 +1757,11 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_R
for (const auto &i: req.outputs)
{
// get tx_hash, tx_out_index from DB
- crypto::public_key key = m_db->get_output_key(i.amount, i.index).pubkey;
+ const output_data_t &od = m_db->get_output_key(i.amount, i.index);
tx_out_index toi = m_db->get_output_tx_and_index(i.amount, i.index);
bool unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
- res.outs.push_back({key, unlocked});
+ res.outs.push_back({od.pubkey, od.commitment, unlocked});
}
return true;
}
@@ -2012,7 +2162,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
// This function overloads its sister function with
// an extra value (hash of highest block that holds an output used as input)
// as a return-by-reference.
-bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
+bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2055,9 +2205,24 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from hard fork 2, we forbid dust and compound outputs
if (m_hardfork->get_current_version() >= 2) {
for (auto &o: tx.vout) {
- if (!is_valid_decomposed_amount(o.amount)) {
- tvc.m_invalid_output = true;
- return false;
+ if (tx.version == 1)
+ {
+ if (!is_valid_decomposed_amount(o.amount)) {
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+ }
+ }
+
+ // in a v2 tx, all outputs must have 0 amount
+ if (m_hardfork->get_current_version() >= 3) {
+ if (tx.version >= 2) {
+ for (auto &o: tx.vout) {
+ if (o.amount != 0) {
+ tvc.m_invalid_output = true;
+ return false;
+ }
}
}
}
@@ -2076,13 +2241,83 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
}
return false;
}
+bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys)
+{
+ CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2");
+
+ rct::rctSig &rv = tx.rct_signatures;
+
+ // message - hash of the transaction prefix
+ rv.message = rct::hash2rct(tx_prefix_hash);
+
+ // mixRing - full and simple store it in opposite ways
+ if (rv.type == rct::RCTTypeFull)
+ {
+ rv.mixRing.resize(pubkeys[0].size());
+ for (size_t m = 0; m < pubkeys[0].size(); ++m)
+ rv.mixRing[m].clear();
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ CHECK_AND_ASSERT_MES(pubkeys[n].size() <= pubkeys[0].size(), false, "More inputs that first ring");
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ rv.mixRing[m].push_back(pubkeys[n][m]);
+ }
+ }
+ }
+ else if (rv.type == rct::RCTTypeSimple)
+ {
+ rv.mixRing.resize(pubkeys.size());
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ rv.mixRing[n].clear();
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ rv.mixRing[n].push_back(pubkeys[n][m]);
+ }
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
+ }
+
+ // II
+ if (rv.type == rct::RCTTypeFull)
+ {
+ rv.p.MGs.resize(1);
+ rv.p.MGs[0].II.resize(tx.vin.size());
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
+ else if (rv.type == rct::RCTTypeSimple)
+ {
+ CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ rv.p.MGs[n].II.resize(1);
+ rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
+ }
+
+ // outPk
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == tx.vout.size(), false, "Bad outPk size");
+ for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
+ rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
+
+ return true;
+}
//------------------------------------------------------------------
// This function validates transaction inputs and their keys.
// FIXME: consider moving functionality specific to one input into
// check_tx_input() rather than here, and use this function simply
// to iterate the inputs as necessary (splitting the task
// using threads, etc.)
-bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
+bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t sig_index = 0;
@@ -2091,30 +2326,44 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
+ const uint8_t hf_version = m_hardfork->get_current_version();
+
// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
- if (m_hardfork->get_current_version() >= 2)
+ if (hf_version >= 2)
{
size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max();
+ const size_t min_mixin = hf_version >= 5 ? 4 : 2;
for (const auto& txin : tx.vin)
{
// non txin_to_key inputs will be rejected below
if (txin.type() == typeid(txin_to_key))
{
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
- uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
- LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available");
- // n_outputs includes the output we're considering
- if (n_outputs <= 2)
- ++n_unmixable;
- else
+ if (in_to_key.amount == 0)
+ {
+ // always consider rct inputs mixable. Even if there's not enough rct
+ // inputs on the chain to mix with, this is going to be the case for
+ // just a few blocks right after the fork at most
++n_mixable;
+ }
+ else
+ {
+ uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
+ LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available");
+ // n_outputs includes the output we're considering
+ if (n_outputs <= min_mixin)
+ ++n_unmixable;
+ else
+ ++n_mixable;
+ }
if (in_to_key.key_offsets.size() - 1 < mixin)
mixin = in_to_key.key_offsets.size() - 1;
}
}
- if (mixin < 2)
+
+ if (mixin < min_mixin)
{
if (n_unmixable == 0)
{
@@ -2129,6 +2378,22 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
}
+
+ // min/max tx version based on HF, and we accept v1 txes if having a non mixable
+ const size_t max_tx_version = (hf_version <= 3) ? 1 : 2;
+ if (tx.version > max_tx_version)
+ {
+ LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version);
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+ const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1);
+ if (tx.version < min_tx_version)
+ {
+ LOG_PRINT_L1("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version);
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
}
auto it = m_check_txin_table.find(tx_prefix_hash);
@@ -2140,7 +2405,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
}
uint64_t t_t1 = 0;
- std::vector<std::vector<crypto::public_key>> pubkeys(tx.vin.size());
+ std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
@@ -2189,28 +2454,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
- // basically, make sure number of inputs == number of signatures
- CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
+ if (tx.version == 1)
+ {
+ // basically, make sure number of inputs == number of signatures
+ CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
#if defined(CACHE_VIN_RESULTS)
- auto itk = it->second.find(in_to_key.k_image);
- if(itk != it->second.end())
- {
- if(!itk->second)
+ auto itk = it->second.find(in_to_key.k_image);
+ if(itk != it->second.end())
{
- LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
- return false;
- }
+ if(!itk->second)
+ {
+ LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
+ return false;
+ }
- // txin has been verified already, skip
- sig_index++;
- continue;
- }
+ // txin has been verified already, skip
+ sig_index++;
+ continue;
+ }
#endif
+ }
// make sure that output being spent matches up correctly with the
// signature spending it.
- if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pubkeys[sig_index], pmax_used_block_height))
+ if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
{
it->second[in_to_key.k_image] = false;
LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
@@ -2222,28 +2490,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}
- if (threads > 1)
+ if (tx.version == 1)
{
- // ND: Speedup
- // 1. Thread ring signature verification if possible.
- ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
- }
- else
- {
- check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
- if (!results[sig_index])
+ if (threads > 1)
{
- it->second[in_to_key.k_image] = false;
- LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
-
- if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
+ // ND: Speedup
+ // 1. Thread ring signature verification if possible.
+ ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
+ }
+ else
+ {
+ check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
+ if (!results[sig_index])
{
- LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
- }
+ it->second[in_to_key.k_image] = false;
+ LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
- return false;
+ if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
+ {
+ LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
+ }
+
+ return false;
+ }
+ it->second[in_to_key.k_image] = true;
}
- it->second[in_to_key.k_image] = true;
}
sig_index++;
@@ -2251,30 +2522,170 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
KILL_IOSERVICE();
- if (threads > 1)
+ if (tx.version == 1)
+ {
+ if (threads > 1)
+ {
+ // save results to table, passed or otherwise
+ bool failed = false;
+ for (size_t i = 0; i < tx.vin.size(); i++)
+ {
+ const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
+ it->second[in_to_key.k_image] = results[i];
+ if(!failed && !results[i])
+ failed = true;
+ }
+
+ if (failed)
+ {
+ LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
+ return false;
+ }
+ }
+ }
+ else
{
- // save results to table, passed or otherwise
- bool failed = false;
- for (size_t i = 0; i < tx.vin.size(); i++)
+ if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys))
{
- const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
- it->second[in_to_key.k_image] = results[i];
- if(!failed && !results[i])
- failed = true;
+ LOG_PRINT_L1("Failed to expand rct signatures!");
+ return false;
}
- if (failed)
+ // from version 2, check ringct signatures
+ // obviously, the original and simple rct APIs use a mixRing that's indexes
+ // in opposite orders, because it'd be too simple otherwise...
+ const rct::rctSig &rv = tx.rct_signatures;
+ switch (rv.type)
{
- LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
+ case rct::RCTTypeNull: {
+ // we only accept no signatures for coinbase txes
+ LOG_PRINT_L1("Null rct signature on non-coinbase tx");
+ return false;
+ }
+ case rct::RCTTypeSimple: {
+ // check all this, either recontructed (so should really pass), or not
+ {
+ if (pubkeys.size() != rv.mixRing.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+ for (size_t i = 0; i < pubkeys.size(); ++i)
+ {
+ if (pubkeys[i].size() != rv.mixRing[i].size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+ }
+
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[n][m].dest))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
+ return false;
+ }
+ if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[n][m].mask))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (rv.p.MGs.size() != tx.vin.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes");
+ return false;
+ }
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image");
+ return false;
+ }
+ }
+
+ if (!rct::verRctSimple(rv))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures!");
+ return false;
+ }
+ break;
+ }
+ case rct::RCTTypeFull: {
+ // check all this, either recontructed (so should really pass), or not
+ {
+ bool size_matches = true;
+ for (size_t i = 0; i < pubkeys.size(); ++i)
+ size_matches &= pubkeys[i].size() == rv.mixRing.size();
+ for (size_t i = 0; i < rv.mixRing.size(); ++i)
+ size_matches &= pubkeys.size() == rv.mixRing[i].size();
+ if (!size_matches)
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
+ return false;
+ }
+
+ for (size_t n = 0; n < pubkeys.size(); ++n)
+ {
+ for (size_t m = 0; m < pubkeys[n].size(); ++m)
+ {
+ if (pubkeys[n][m].dest != rct::rct2pk(rv.mixRing[m][n].dest))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
+ return false;
+ }
+ if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[m][n].mask))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
+ return false;
+ }
+ }
+ }
+ }
+
+ if (rv.p.MGs.size() != 1)
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: Bad MGs size");
+ return false;
+ }
+ if (rv.p.MGs[0].II.size() != tx.vin.size())
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
+ return false;
+ }
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[0].II[n], 32))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes");
+ return false;
+ }
+ }
+
+ if (!rct::verRct(rv))
+ {
+ LOG_PRINT_L1("Failed to check ringct signatures!");
+ return false;
+ }
+ break;
+ }
+ default:
+ LOG_PRINT_L1("Unsupported rct type: " << rv.type);
return false;
}
}
- LOG_PRINT_L1("t_loop: " << t_t1);
return true;
}
//------------------------------------------------------------------
-void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
+void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
{
if (m_is_in_checkpoint_zone)
{
@@ -2285,7 +2696,8 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
std::vector<const crypto::public_key *> p_output_keys;
for (auto &key : pubkeys)
{
- p_output_keys.push_back(&key);
+ // rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy
+ p_output_keys.push_back(&(const crypto::public_key&)key.dest);
}
result = crypto::check_ring_signature(tx_prefix_hash, key_image, p_output_keys, sig.data()) ? 1 : 0;
@@ -2321,7 +2733,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
// This function locates all outputs associated with a given input (mixins)
// and validates that they exist and are usable. It also checks the ring
// signature for each input.
-bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height)
+bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -2331,13 +2743,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
struct outputs_visitor
{
- std::vector<crypto::public_key >& m_output_keys;
+ std::vector<rct::ctkey >& m_output_keys;
const Blockchain& m_bch;
- outputs_visitor(std::vector<crypto::public_key>& output_keys, const Blockchain& bch) :
+ outputs_visitor(std::vector<rct::ctkey>& output_keys, const Blockchain& bch) :
m_output_keys(output_keys), m_bch(bch)
{
}
- bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey)
+ bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey, const rct::key &commitment)
{
//check tx unlock time
if (!m_bch.is_tx_spendtime_unlocked(unlock_time))
@@ -2351,7 +2763,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
// but only txout_to_key outputs are stored in the DB in the first place, done in
// Blockchain*::add_output
- m_output_keys.push_back(pubkey);
+ m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment}));
return true;
}
};
@@ -2360,7 +2772,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
// collect output keys
outputs_visitor vi(output_keys, *this);
- if (!scan_outputkeys_for_indexes(txin, vi, tx_prefix_hash, pmax_related_block_height))
+ if (!scan_outputkeys_for_indexes(tx_version, txin, vi, tx_prefix_hash, pmax_related_block_height))
{
LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
return false;
@@ -2371,7 +2783,10 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
return false;
}
- CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
+ if (tx_version == 1) {
+ CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
+ }
+ // rct_signatures will be expanded after this
return true;
}
//------------------------------------------------------------------
@@ -2453,6 +2868,8 @@ void Blockchain::return_tx_to_pool(const std::vector<transaction> &txs)
//------------------------------------------------------------------
bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
{
+ CRITICAL_REGION_LOCAL(m_tx_pool);
+
bool res = true;
for (const auto &txid: txids)
{
@@ -2461,7 +2878,7 @@ bool Blockchain::flush_txes_from_pool(const std::list<crypto::hash> &txids)
uint64_t fee;
bool relayed;
LOG_PRINT_L1("Removing txid " << txid << " from the pool");
- if(!m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed))
+ if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed))
{
LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool");
res = false;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 1524693b8..94701608e 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -455,6 +455,23 @@ namespace cryptonote
bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const;
/**
+ * @brief gets random ringct outputs to mix with
+ *
+ * This function takes an RPC request for outputs to mix with
+ * and creates an RPC response with the resultant output indices
+ * and the matching keys.
+ *
+ * Outputs to mix with are randomly selected from the utxo set
+ * for each output amount in the request.
+ *
+ * @param req the output amounts and number of mixins to select
+ * @param res return-by-reference the resultant output indices
+ *
+ * @return true
+ */
+ bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
+
+ /**
* @brief gets the global indices for outputs from a given transaction
*
* This function gets the global indices for all outputs belonging
@@ -483,6 +500,7 @@ namespace cryptonote
* validates a transaction's inputs as correctly used and not previously
* spent. also returns the hash and height of the most recent block
* which contains an output that was used as an input to the transaction.
+ * The transaction's rct signatures, if any, are expanded.
*
* @param tx the transaction to validate
* @param pmax_used_block_height return-by-reference block height of most recent input
@@ -492,7 +510,7 @@ namespace cryptonote
*
* @return false if any input is invalid, otherwise true
*/
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
+ bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
/**
* @brief check that a transaction's outputs conform to current standards
@@ -867,11 +885,12 @@ namespace cryptonote
* @param vis an instance of the visitor to use
* @param tx_prefix_hash the hash of the associated transaction_prefix
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
+ * @param tx_version version of the tx, if > 1 we also get commitments
*
* @return false if any keys are not found or any inputs are not unlocked, otherwise true
*/
template<class visitor_t>
- inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
+ inline bool scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
/**
* @brief collect output public keys of a transaction input set
@@ -883,15 +902,17 @@ namespace cryptonote
* If pmax_related_block_height is not NULL, its value is set to the height
* of the most recent block which contains an output used in the input set
*
+ * @param tx_version the transaction version
* @param txin the transaction input
* @param tx_prefix_hash the transaction prefix hash, for caching organization
* @param sig the input signature
* @param output_keys return-by-reference the public keys of the outputs in the input set
+ * @param rct_signatures the ringCT signatures, which are only valid if tx version > 1
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
*
* @return false if any output is not yet unlocked, or is missing, otherwise true
*/
- bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height);
+ bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height);
/**
* @brief validate a transaction's inputs and their keys
@@ -899,6 +920,7 @@ namespace cryptonote
* This function validates transaction inputs and their keys. Previously
* it also performed double spend checking, but that has been moved to its
* own function.
+ * The transaction's rct signatures, if any, are expanded.
*
* If pmax_related_block_height is not NULL, its value is set to the height
* of the most recent block which contains an output used in any input set
@@ -912,7 +934,7 @@ namespace cryptonote
*
* @return false if any validation step fails, otherwise true
*/
- bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
+ bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
/**
* @brief performs a blockchain reorganization according to the longest chain rule
@@ -1054,6 +1076,15 @@ namespace cryptonote
void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
/**
+ * @brief adds the given output to the requested set of random ringct outputs
+ *
+ * @param outs return-by-reference the set the output is to be added to
+ * @param amount the output amount (0 for rct inputs)
+ * @param i the rct output index
+ */
+ void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const;
+
+ /**
* @brief checks if a transaction is unlocked (its outputs spendable)
*
* This function checks to see if a transaction is unlocked.
@@ -1172,7 +1203,7 @@ namespace cryptonote
* @param result false if the ring signature is invalid, otherwise true
*/
void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image,
- const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
+ const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
/**
* @brief loads block hashes from compiled-in data set
@@ -1182,5 +1213,14 @@ namespace cryptonote
* a useful state.
*/
void load_compiled_in_block_hashes();
+
+ /**
+ * @brief expands v2 transaction data from blockchain
+ *
+ * RingCT transactions do not transmit some of their data if it
+ * can be reconstituted by the receiver. This function expands
+ * that implicit data.
+ */
+ bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
};
} // namespace cryptonote
diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp
deleted file mode 100644
index a829b7cbe..000000000
--- a/src/cryptonote_core/blockchain_storage.cpp
+++ /dev/null
@@ -1,1952 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#include <algorithm>
-#include <cstdio>
-#include <cmath>
-#include <boost/archive/binary_oarchive.hpp>
-#include <boost/archive/binary_iarchive.hpp>
-
-#include "include_base_utils.h"
-#include "cryptonote_basic_impl.h"
-#include "blockchain_storage.h"
-#include "cryptonote_format_utils.h"
-#include "cryptonote_boost_serialization.h"
-#include "blockchain_storage_boost_serialization.h"
-#include "cryptonote_config.h"
-#include "miner.h"
-#include "misc_language.h"
-#include "profile_tools.h"
-#include "file_io_utils.h"
-#include "common/boost_serialization_helper.h"
-#include "warnings.h"
-#include "crypto/hash.h"
-#include "cryptonote_core/checkpoints.h"
-//#include "serialization/json_archive.h"
-#include "../../contrib/otshell_utils/utils.hpp"
-#include "../../src/p2p/data_logger.hpp"
-
-using namespace cryptonote;
-
-DISABLE_VS_WARNINGS(4267)
-
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx(const crypto::hash &id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_transactions.find(id) != m_transactions.end();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_spent_keys.find(key_im) != m_spent_keys.end();
-}
-//------------------------------------------------------------------
-const transaction *blockchain_storage::get_tx(const crypto::hash &id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_transactions.find(id);
- if (it == m_transactions.end())
- return NULL;
-
- return &it->second.tx;
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_blockchain_height() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_blocks.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::init(const std::string& config_folder, bool testnet)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_config_folder = config_folder;
- m_testnet = testnet;
- LOG_PRINT_L0("Loading blockchain...");
- const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
- if(tools::unserialize_obj_from_file(*this, filename))
- {
-
- // checkpoints
-
- // mainchain
- for (size_t height=0; height < m_blocks.size(); ++height)
- {
- CHECK_AND_ASSERT_MES((!m_checkpoints.is_in_checkpoint_zone(height)) || m_checkpoints.check_block(height,get_block_hash(m_blocks[height].bl)),false,"checkpoint fail, blockchain.bin invalid");
- }
-
- // check alt chains
- #if 0
- // doesn't work when a checkpoint is added and there are already alt chains. However, the rest of the blockchain code suffers from the same issue, so ignore for now
- // see issue #118
- BOOST_FOREACH(blocks_ext_by_hash::value_type& alt_block, m_alternative_chains)
- {
- CHECK_AND_ASSERT_MES(m_checkpoints.is_alternative_block_allowed(m_blocks.size()-1,alt_block.second.height),false,"stored alternative block not allowed, blockchain.bin invalid");
- }
- #endif
- }
- else
- {
- LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block.");
- if (!store_genesis_block(testnet, true))
- return false;
- }
- if(!m_blocks.size())
- {
- LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
-
- if (!store_genesis_block(testnet, false)) {
- return false;
- }
- } else {
- cryptonote::block b;
-
- if (testnet)
- {
- generate_genesis_block(b, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
- }
-
- crypto::hash genesis_hash = get_block_hash(m_blocks[0].bl);
- crypto::hash testnet_genesis_hash = get_block_hash(b);
- if (genesis_hash != testnet_genesis_hash) {
- LOG_ERROR("Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network.");
- return false;
- }
- }
- uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp;
- if(!m_blocks.back().bl.timestamp)
- timestamp_diff = time(NULL) - 1341378000;
- LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::store_genesis_block(bool testnet, bool check_added) {
- block bl = ::boost::value_initialized<block>();
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
-
- if (testnet)
- {
- generate_genesis_block(bl, config::testnet::GENESIS_TX, config::testnet::GENESIS_NONCE);
- }
- else
- {
- generate_genesis_block(bl, config::GENESIS_TX, config::GENESIS_NONCE);
- }
-
- add_new_block(bl, bvc);
- CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && (bvc.m_added_to_main_chain || !check_added), false, "Failed to add genesis block to blockchain");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::store_blockchain()
-{
- m_is_blockchain_storing = true;
- epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_is_blockchain_storing=false;});
-
- LOG_PRINT_L0("Storing blockchain...");
- if (!tools::create_directories_if_necessary(m_config_folder))
- {
- LOG_PRINT_L0("Failed to create data directory: " << m_config_folder);
- return false;
- }
-
- const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME;
- // There is a chance that temp_filename and filename are hardlinks to the same file
- std::remove(temp_filename.c_str());
- if(!tools::serialize_obj_to_file(*this, temp_filename))
- {
- //achtung!
- LOG_ERROR("Failed to save blockchain data to file: " << temp_filename);
- return false;
- }
- const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
- std::error_code ec = tools::replace_file(temp_filename, filename);
- if (ec)
- {
- LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value());
- return false;
- }
- LOG_PRINT_L0("Blockchain stored OK.");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::deinit()
-{
- return store_blockchain();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::pop_block_from_blockchain()
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size());
- size_t h = m_blocks.size()-1;
- block_extended_info& bei = m_blocks[h];
- //crypto::hash id = get_block_hash(bei.bl);
- bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size());
- CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h);
-
- //remove from index
- auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl));
- CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index");
- m_blocks_index.erase(bl_ind);
- //pop block from core
- m_blocks.pop_back();
- m_tx_pool->on_blockchain_dec(m_blocks.size()-1, get_tail_id());
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::reset_and_set_genesis_block(const block& b)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_transactions.clear();
- m_spent_keys.clear();
- m_blocks.clear();
- m_blocks_index.clear();
- m_alternative_chains.clear();
- m_outputs.clear();
-
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- add_new_block(b, bvc);
- return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- struct purge_transaction_visitor: public boost::static_visitor<bool>
- {
- key_images_container& m_spent_keys;
- bool m_strict_check;
- purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){}
-
- bool operator()(const txin_to_key& inp) const
- {
- //const crypto::key_image& ki = inp.k_image;
- auto r = m_spent_keys.find(inp.k_image);
- if(r != m_spent_keys.end())
- {
- m_spent_keys.erase(r);
- }else
- {
- CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found");
- }
- return true;
- }
- bool operator()(const txin_gen& inp) const
- {
- return true;
- }
- bool operator()(const txin_to_script& tx) const
- {
- return false;
- }
-
- bool operator()(const txin_to_scripthash& tx) const
- {
- return false;
- }
- };
-
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in);
- CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor");
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto tx_index_it = m_transactions.find(tx_id);
- CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!");
- transaction& tx = tx_index_it->second.tx;
-
- purge_transaction_keyimages_from_blockchain(tx, true);
-
- if(!is_coinbase(tx))
- {
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool r = m_tx_pool->add_tx(tx, tvc, true, true, 1);
- CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool");
- }
-
- bool res = pop_transaction_from_global_index(tx, tx_id);
- m_transactions.erase(tx_index_it);
- LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL);
- return res;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- bool res = true;
- CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain");
- for(size_t count = 0; count != processed_tx_count; count++)
- {
- res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res;
- }
-
- res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res;
-
- return res;
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id(uint64_t& height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = get_current_blockchain_height()-1;
- return get_tail_id();
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_tail_id() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- crypto::hash id = null_hash;
- if(m_blocks.size())
- {
- get_block_hash(m_blocks.back().bl, id);
- }
- return id;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_short_chain_history(std::list<crypto::hash>& ids) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = 0;
- size_t current_multiplier = 1;
- size_t sz = m_blocks.size();
- if(!sz)
- return true;
- size_t current_back_offset = 1;
- bool genesis_included = false;
- while(current_back_offset < sz)
- {
- ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl));
- if(sz-current_back_offset == 0)
- genesis_included = true;
- if(i < 10)
- {
- ++current_back_offset;
- }else
- {
- current_back_offset += current_multiplier *= 2;
- }
- ++i;
- }
- if(!genesis_included)
- ids.push_back(get_block_hash(m_blocks[0].bl));
-
- return true;
-}
-//------------------------------------------------------------------
-crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(height >= m_blocks.size())
- return null_hash;
-
- return get_block_hash(m_blocks[height].bl);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) const {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- // try to find block in main chain
- blocks_by_id_index::const_iterator it = m_blocks_index.find(h);
- if (m_blocks_index.end() != it) {
- blk = m_blocks[it->second].bl;
- return true;
- }
-
- // try to find block in alternative chain
- blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h);
- if (m_alternative_chains.end() != it_alt) {
- blk = it_alt->second.bl;
- return true;
- }
-
- return false;
-}
-//------------------------------------------------------------------
-void blockchain_storage::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const blocks_by_id_index::value_type &v, m_blocks_index)
- main.push_back(v.first);
-
- BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_alternative_chains)
- alt.push_back(v.first);
-
- BOOST_FOREACH(const blocks_ext_by_hash::value_type &v, m_invalid_blocks)
- invalid.push_back(v.first);
-}
-//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_difficulty_for_next_block() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- std::vector<uint64_t> timestamps;
- std::vector<difficulty_type> commulative_difficulties;
- size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT));
- if(!offset)
- ++offset;//skip genesis block
- for(; offset < m_blocks.size(); offset++)
- {
- timestamps.push_back(m_blocks[offset].bl.timestamp);
- commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty);
- }
- return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- // fail if rollback_height passed is too high
- if (rollback_height > m_blocks.size())
- {
- return true;
- }
-
- //remove failed subchain
- for(size_t i = m_blocks.size()-1; i >=rollback_height; i--)
- {
- bool r = pop_block_from_blockchain();
- CHECK_AND_ASSERT_MES(r, false, "PANIC! failed to remove block while chain switching during the rollback!");
- }
- //return back original chain
- BOOST_FOREACH(auto& bl, original_chain)
- {
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_block_to_main_chain(bl, bvc);
- CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
- }
-
- LOG_PRINT_L1("Rollback success.");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
-
- size_t split_height = alt_chain.front()->second.height;
- CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height");
-
- //disconnecting old chain
- std::list<block> disconnected_chain;
- for(size_t i = m_blocks.size()-1; i >=split_height; i--)
- {
- block b = m_blocks[i].bl;
- bool r = pop_block_from_blockchain();
- CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching");
- disconnected_chain.push_front(b);
- }
-
- //connecting new alternative chain
- for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
- {
- auto ch_ent = *alt_ch_iter;
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
- if(!r || !bvc.m_added_to_main_chain)
- {
- LOG_PRINT_L1("Failed to switch to alternative blockchain");
- rollback_blockchain_switching(disconnected_chain, split_height);
- add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
- LOG_PRINT_L1("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl));
- m_alternative_chains.erase(ch_ent);
-
- for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++)
- {
- //block_verification_context bvc = boost::value_initialized<block_verification_context>();
- add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first);
- m_alternative_chains.erase(*alt_ch_to_orph_iter);
- }
- return false;
- }
- }
-
- if(!discard_disconnected_chain)
- {
- //pushing old chain as alternative chain
- BOOST_FOREACH(auto& old_ch_ent, disconnected_chain)
- {
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
- bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
- if(!r)
- {
- LOG_PRINT_L1("Failed to push ex-main chain blocks to alternative chain ");
- rollback_blockchain_switching(disconnected_chain, split_height);
- return false;
- }
- }
- }
-
- //removing all_chain entries from alternative chain
- BOOST_FOREACH(auto ch_ent, alt_chain)
- {
- m_alternative_chains.erase(ch_ent);
- }
-
- LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(), LOG_LEVEL_0);
- return true;
-}
-//------------------------------------------------------------------
-difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
-{
- std::vector<uint64_t> timestamps;
- std::vector<difficulty_type> commulative_difficulties;
- if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT)
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
- size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
- main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
- size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
-
- if(!main_chain_start_offset)
- ++main_chain_start_offset; //skip genesis block
- for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset)
- {
- timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp);
- commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty);
- }
-
- CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size()
- << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT );
- BOOST_FOREACH(auto it, alt_chain)
- {
- timestamps.push_back(it->second.bl.timestamp);
- commulative_difficulties.push_back(it->second.cumulative_difficulty);
- }
- }else
- {
- timestamps.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT)));
- commulative_difficulties.resize(std::min(alt_chain.size(), static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT)));
- size_t count = 0;
- size_t max_i = timestamps.size()-1;
- BOOST_REVERSE_FOREACH(auto it, alt_chain)
- {
- timestamps[max_i - count] = it->second.bl.timestamp;
- commulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
- count++;
- if(count >= DIFFICULTY_BLOCKS_COUNT)
- break;
- }
- }
- return next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) const
-{
- CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs");
- CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
- if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height)
- {
- LOG_PRINT_RED_L1("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height);
- return false;
- }
- CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW,
- false,
- "coinbase transaction transaction has the wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
-
- //check outs overflow
- if(!check_outs_overflow(b.miner_tx))
- {
- LOG_PRINT_RED_L1("miner transaction has money overflow in block " << get_block_hash(b));
- return false;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const
-{
- //validate reward
- uint64_t money_in_use = 0;
- BOOST_FOREACH(auto& o, b.miner_tx.vout)
- money_in_use += o.amount;
-
- std::vector<size_t> last_blocks_sizes;
- get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward,0)) {
- LOG_PRINT_L1("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
- return false;
- }
- if(base_reward + fee < money_in_use)
- {
- LOG_PRINT_L1("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
- return false;
- }
- if(base_reward + fee != money_in_use)
- {
- LOG_PRINT_L1("coinbase transaction doesn't use full amount of block reward: spent: "
- << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size());
-
- size_t start_offset = (from_height+1) - std::min((from_height+1), count);
- for(size_t i = start_offset; i != from_height+1; i++)
- sz.push_back(m_blocks[i].block_cumulative_size);
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!m_blocks.size())
- return true;
- return get_backward_blocks_sizes(m_blocks.size() -1, sz, count);
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_current_cumulative_blocksize_limit() const
-{
- return m_current_block_cumul_sz_limit;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) const
-{
- size_t median_size;
- uint64_t already_generated_coins;
-
- CRITICAL_REGION_BEGIN(m_blockchain_lock);
- b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
- b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
- b.prev_id = get_tail_id();
- b.timestamp = time(NULL);
- height = m_blocks.size();
- diffic = get_difficulty_for_next_block();
- CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
-
- median_size = m_current_block_cumul_sz_limit / 2;
- already_generated_coins = m_blocks.back().already_generated_coins;
-
- CRITICAL_REGION_END();
-
- size_t txs_size;
- uint64_t fee;
- if (!m_tx_pool->fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
- return false;
- }
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- size_t real_txs_size = 0;
- uint64_t real_fee = 0;
- CRITICAL_REGION_BEGIN(m_tx_pool->m_transactions_lock);
- BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) {
- auto cur_res = m_tx_pool->m_transactions.find(cur_hash);
- if (cur_res == m_tx_pool->m_transactions.end()) {
- LOG_ERROR("Creating block template: error: transaction not found");
- continue;
- }
- tx_memory_pool::tx_details &cur_tx = cur_res->second;
- real_txs_size += cur_tx.blob_size;
- real_fee += cur_tx.fee;
- if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) {
- LOG_ERROR("Creating block template: error: invalid transaction size");
- }
- uint64_t inputs_amount;
- if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) {
- LOG_ERROR("Creating block template: error: cannot get inputs amount");
- } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) {
- LOG_ERROR("Creating block template: error: invalid fee");
- }
- }
- if (txs_size != real_txs_size) {
- LOG_ERROR("Creating block template: error: wrongly calculated transaction size");
- }
- if (fee != real_fee) {
- LOG_ERROR("Creating block template: error: wrongly calculated fee");
- }
- CRITICAL_REGION_END();
- LOG_PRINT_L1("Creating block template: height " << height <<
- ", median size " << median_size <<
- ", already generated coins " << already_generated_coins <<
- ", transaction size " << txs_size <<
- ", fee " << fee);
-#endif
-
- /*
- two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know
- block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
- */
- //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
- bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
- CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
- size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) <<
- ", cumulative size " << cumulative_size);
-#endif
- for (size_t try_count = 0; try_count != 10; ++try_count) {
- r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
-
- CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
- size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
- if (coinbase_blob_size > cumulative_size - txs_size) {
- cumulative_size = txs_size + coinbase_blob_size;
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << cumulative_size << " is greater then before");
-#endif
- continue;
- }
-
- if (coinbase_blob_size < cumulative_size - txs_size) {
- size_t delta = cumulative_size - txs_size - coinbase_blob_size;
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << txs_size + coinbase_blob_size <<
- " is less then before, adding " << delta << " zero bytes");
-#endif
- b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
- //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
- if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
- CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
- b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
- if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
- //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
- LOG_PRINT_RED("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
- cumulative_size += delta - 1;
- continue;
- }
- LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1);
- }
- }
- CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size <<
- ", cumulative size " << cumulative_size << " is now good");
-#endif
- return true;
- }
- LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
-{
-
- if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
- return true;
-
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
- CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size());
- size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0;
- do
- {
- timestamps.push_back(m_blocks[start_top_height].bl.timestamp);
- if(start_top_height == 0)
- break;
- --start_top_height;
- }while(start_top_height != stop_offset);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- uint64_t block_height = get_block_height(b);
- if(0 == block_height)
- {
- LOG_PRINT_L1("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has wrong miner transaction");
- bvc.m_verifivation_failed = true;
- return false;
- }
- if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " can't be accepted for alternative chain, block height: " << block_height
- << ENDL << " blockchain height: " << get_current_blockchain_height());
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- //Block is not related with head of main chain
- //first of all - look in alternative chains container
- auto it_main_prev = m_blocks_index.find(b.prev_id);
- auto it_prev = m_alternative_chains.find(b.prev_id);
- if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end())
- {
- //we have new block in alternative chain
-
- //build alternative subchain, front -> mainchain, back -> alternative head
- blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
- std::list<blocks_ext_by_hash::iterator> alt_chain;
- std::vector<uint64_t> timestamps;
- while(alt_it != m_alternative_chains.end())
- {
- alt_chain.push_front(alt_it);
- timestamps.push_back(alt_it->second.bl.timestamp);
- alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
- }
-
- if(alt_chain.size())
- {
- //make sure that it has right connection to main chain
- CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height");
- crypto::hash h = null_hash;
- get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h);
- CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
- complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps);
- }else
- {
- CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()");
- complete_timestamps_vector(it_main_prev->second, timestamps);
- }
- //check timestamp correct
- if(!check_block_timestamp(timestamps, b))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " for alternative chain, has invalid timestamp: " << b.timestamp);
- //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = b;
- bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1;
-
- bool is_a_checkpoint;
- if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
- {
- LOG_ERROR("CHECKPOINT VALIDATION FAILED");
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- // Always check PoW for alternative blocks
- m_is_in_checkpoint_zone = false;
- difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
- CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
- crypto::hash proof_of_work = null_hash;
- get_block_longhash(bei.bl, proof_of_work, bei.height);
- if(!check_hash(proof_of_work, current_diff))
- {
- LOG_PRINT_RED_L1("Block with id: " << id
- << ENDL << " for alternative chain, does not have enough proof of work: " << proof_of_work
- << ENDL << " expected difficulty: " << current_diff);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- if(!prevalidate_miner_transaction(b, bei.height))
- {
- LOG_PRINT_RED_L1("Block with id: " << epee::string_tools::pod_to_hex(id)
- << " (as alternative) has incorrect miner transaction.");
- bvc.m_verifivation_failed = true;
- return false;
-
- }
-
- bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty;
- bei.cumulative_difficulty += current_diff;
-
-#ifdef _DEBUG
- auto i_dres = m_alternative_chains.find(id);
- CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist");
-#endif
- auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
- CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist");
- alt_chain.push_back(i_res.first);
-
- if(is_a_checkpoint)
- {
- //do reorganize!
- LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 <<
- ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0);
- bool r = switch_to_alternative_blockchain(alt_chain, true);
- if(r) bvc.m_added_to_main_chain = true;
- else bvc.m_verifivation_failed = true;
- return r;
- }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
- {
- //do reorganize!
- LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty
- << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0);
- bool r = switch_to_alternative_blockchain(alt_chain, false);
- if(r) bvc.m_added_to_main_chain = true;
- else bvc.m_verifivation_failed = true;
- return r;
- }else
- {
- LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height
- << ENDL << "id:\t" << id
- << ENDL << "PoW:\t" << proof_of_work
- << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0);
- return true;
- }
- }else
- {
- //block orphaned
- bvc.m_marked_as_orphaned = true;
- LOG_PRINT_RED_L1("Block recognized as orphaned and rejected, id = " << id);
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_offset >= m_blocks.size())
- return false;
- for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++)
- {
- blocks.push_back(m_blocks[i].bl);
- std::list<crypto::hash> missed_ids;
- get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids);
- CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain");
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_offset >= m_blocks.size())
- return false;
-
- for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++)
- blocks.push_back(m_blocks[i].bl);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- rsp.current_blockchain_height = get_current_blockchain_height();
- std::list<block> blocks;
- get_blocks(arg.blocks, blocks, rsp.missed_ids);
-
- BOOST_FOREACH(const auto& bl, blocks)
- {
- std::list<crypto::hash> missed_tx_id;
- std::list<transaction> txs;
- get_transactions(bl.tx_hashes, txs, rsp.missed_ids);
- CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: has missed missed_tx_id.size()=" << missed_tx_id.size()
- << ENDL << "for block id = " << get_block_hash(bl));
- rsp.blocks.push_back(block_complete_entry());
- block_complete_entry& e = rsp.blocks.back();
- //pack block
- e.block = t_serializable_object_to_blob(bl);
- //pack transactions
- BOOST_FOREACH(transaction& tx, txs)
- e.txs.push_back(t_serializable_object_to_blob(tx));
-
- }
- //get another transactions, if need
- std::list<transaction> txs;
- get_transactions(arg.txs, txs, rsp.missed_ids);
- //pack aside transactions
- BOOST_FOREACH(const auto& tx, txs)
- rsp.txs.push_back(t_serializable_object_to_blob(tx));
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_alternative_blocks(std::list<block>& blocks) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& alt_bl, m_alternative_chains)
- {
- blocks.push_back(alt_bl.second.bl);
- }
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::get_alternative_blocks_count() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_alternative_chains.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- transactions_container::const_iterator tx_it = m_transactions.find(amount_outs[i].first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL <<
- ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index="
- << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first);
- const transaction& tx = tx_it->second.tx;
- CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type");
-
- //check if transaction is unlocked
- if(!is_tx_spendtime_unlocked(tx.unlock_time))
- return false;
-
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
- oen.global_amount_index = i;
- oen.out_key = boost::get<txout_to_key>(tx.vout[amount_outs[i].second].target).key;
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!amount_outs.size())
- return 0;
- size_t i = amount_outs.size();
- do
- {
- --i;
- transactions_container::const_iterator it = m_transactions.find(amount_outs[i].first);
- CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first);
- if(it->second.m_keeper_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= get_current_blockchain_height() )
- return i+1;
- } while (i != 0);
- return 0;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(uint64_t amount, req.amounts)
- {
- COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
- result_outs.amount = amount;
- auto it = m_outputs.find(amount);
- if(it == m_outputs.end())
- {
- LOG_PRINT_L1("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist");
- continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
- }
- const std::vector<std::pair<crypto::hash, size_t> >& amount_outs = it->second;
- //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
- //lets find upper bound of not fresh outs
- size_t up_index_limit = find_end_of_allowed_index(amount_outs);
- CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size());
- if(amount_outs.size() > req.outs_count)
- {
- std::set<size_t> used;
- size_t try_count = 0;
- for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;)
- {
- // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
- uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
- double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
- size_t i = (size_t)(frac*up_index_limit);
- // just in case rounding up to 1 occurs after sqrt
- if (i == up_index_limit)
- --i;
- if(used.count(i))
- continue;
- bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
- used.insert(i);
- if(added)
- ++j;
- ++try_count;
- }
- }else
- {
- for(size_t i = 0; i != up_index_limit; i++)
- add_out_to_get_random_outs(amount_outs, result_outs, amount, i);
- }
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- if(!qblock_ids.size() /*|| !req.m_total_height*/)
- {
- LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection");
- return false;
- }
- //check genesis match
- if(qblock_ids.back() != get_block_hash(m_blocks[0].bl))
- {
- LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: "
- << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl)
- << "," << ENDL << " dropping connection");
- return false;
- }
-
- /* Figure out what blocks we should request to get state_normal */
- size_t i = 0;
- auto bl_it = qblock_ids.begin();
- auto block_index_it = m_blocks_index.find(*bl_it);
- for(; bl_it != qblock_ids.end(); bl_it++, i++)
- {
- block_index_it = m_blocks_index.find(*bl_it);
- if(block_index_it != m_blocks_index.end())
- break;
- }
-
- if(bl_it == qblock_ids.end())
- {
- LOG_PRINT_L1("Internal error handling connection, can't find split point");
- return false;
- }
-
- if(block_index_it == m_blocks_index.end())
- {
- //this should NEVER happen, but, dose of paranoia in such cases is not too bad
- LOG_PRINT_L1("Internal error handling connection, can't find split point");
- return false;
- }
-
- //we start to put block ids INCLUDING last known id, just to make other side be sure
- starter_offset = block_index_it->second;
- return true;
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::block_difficulty(size_t i) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()");
- if(i == 0)
- return m_blocks[i].cumulative_difficulty;
-
- return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty;
-}
-//------------------------------------------------------------------
-double blockchain_storage::get_avg_block_size( size_t count) const
-{
- if (count > get_current_blockchain_height()) return 500;
-
- double average = 0;
- _dbg1_c("net/blksize", "HEIGHT: " << get_current_blockchain_height());
- _dbg1_c("net/blksize", "BLOCK ID BY HEIGHT: " << get_block_id_by_height(get_current_blockchain_height()) );
- _dbg1_c("net/blksize", "BLOCK TAIL ID: " << get_tail_id() );
- std::vector<size_t> size_vector;
-
- get_backward_blocks_sizes(get_current_blockchain_height() - count, size_vector, count);
-
- std::vector<size_t>::iterator it;
- it = size_vector.begin();
- while (it != size_vector.end()) {
- average += *it;
- _dbg2_c("net/blksize", "VECTOR ELEMENT: " << (*it) );
- it++;
- }
- average = average / count;
- _dbg1_c("net/blksize", "VECTOR SIZE: " << size_vector.size() << " average=" << average);
-
- return average;
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(start_index >=m_blocks.size())
- {
- LOG_PRINT_L1("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1);
- return;
- }
-
- for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++)
- {
- ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size
- << "\nid\t\t" << get_block_hash(m_blocks[i].bl)
- << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL;
- }
- LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str());
- LOG_PRINT_L0("Blockchain printed with log level 1");
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain_index() const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index)
- ss << "id\t\t" << v.first << " height" << v.second << ENDL << "";
-
- LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str());
-}
-//------------------------------------------------------------------
-void blockchain_storage::print_blockchain_outs(const std::string& file) const
-{
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- BOOST_FOREACH(const outputs_container::value_type& v, m_outputs)
- {
- const std::vector<std::pair<crypto::hash, size_t> >& vals = v.second;
- if(vals.size())
- {
- ss << "amount: " << v.first << ENDL;
- for(size_t i = 0; i != vals.size(); i++)
- ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL;
- }
- }
- if(epee::file_io_utils::save_string_to_file(file, ss.str()))
- {
- LOG_PRINT_L0("Current outputs index writen to file: " << file);
- }else
- {
- LOG_PRINT_L0("Failed to write current outputs index to file: " << file);
- }
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(!find_blockchain_supplement(qblock_ids, resp.start_height))
- return false;
-
- resp.total_height = get_current_blockchain_height();
- size_t count = 0;
- for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
- resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl));
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(req_start_block > 0) {
- start_height = req_start_block;
- } else {
- if(!find_blockchain_supplement(qblock_ids, start_height))
- return false;
- }
-
- total_height = get_current_blockchain_height();
- size_t count = 0;
- for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++)
- {
- blocks.resize(blocks.size()+1);
- blocks.back().first = m_blocks[i].bl;
- std::list<crypto::hash> mis;
- get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis);
- CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h)
-{
- block_extended_info bei = AUTO_VAL_INIT(bei);
- bei.bl = bl;
- return add_block_as_invalid(bei, h);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto i_res = m_invalid_blocks.insert(std::map<crypto::hash, block_extended_info>::value_type(h, bei));
- CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed");
- LOG_PRINT_L1("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size());
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_block(const crypto::hash& id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(m_blocks_index.count(id))
- return true;
- if(m_alternative_chains.count(id))
- return true;
- /*if(m_orphaned_blocks.get<by_id>().count(id))
- return true;*/
-
- /*if(m_orphaned_by_tx.count(id))
- return true;*/
- if(m_invalid_blocks.count(id))
- return true;
-
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc)
-{
- crypto::hash id = get_block_hash(bl);
- return handle_block_to_main_chain(bl, id, bvc);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = 0;
- BOOST_FOREACH(const auto& ot, tx.vout)
- {
- outputs_container::mapped_type& amount_index = m_outputs[ot.amount];
- amount_index.push_back(std::pair<crypto::hash, size_t>(tx_id, i));
- global_indexes.push_back(amount_index.size()-1);
- ++i;
- }
- return true;
-}
-//------------------------------------------------------------------
-size_t blockchain_storage::get_total_transactions() const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_transactions.size();
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_outputs.find(amount);
- if(it == m_outputs.end())
- return true;
-
- BOOST_FOREACH(const auto& out_entry, it->second)
- {
- auto tx_it = m_transactions.find(out_entry.first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size");
- CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size");
- pkeys.push_back(boost::get<txout_to_key>(tx_it->second.tx.vout[out_entry.second].target).key);
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- size_t i = tx.vout.size()-1;
- BOOST_REVERSE_FOREACH(const auto& ot, tx.vout)
- {
- auto it = m_outputs.find(ot.amount);
- CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken");
- CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount);
- CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch");
- CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch");
- it->second.pop_back();
- --i;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size)
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- struct add_transaction_input_visitor: public boost::static_visitor<bool>
- {
- key_images_container& m_spent_keys;
- const crypto::hash& m_tx_id;
- const crypto::hash& m_bl_id;
- add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id)
- {}
- bool operator()(const txin_to_key& in) const
- {
- const crypto::key_image& ki = in.k_image;
- auto r = m_spent_keys.insert(ki);
- if(!r.second)
- {
- //double spend detected
- LOG_PRINT_L1("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " has input marked as spent with key image: " << ki << ", block declined");
- return false;
- }
- return true;
- }
-
- bool operator()(const txin_gen& tx) const{return true;}
- bool operator()(const txin_to_script& tx) const{return false;}
- bool operator()(const txin_to_scripthash& tx) const{return false;}
- };
-
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in))
- {
- LOG_PRINT_L1("critical internal error: add_transaction_input_visitor failed. but here key_images should be checked");
- purge_transaction_keyimages_from_blockchain(tx, false);
- return false;
- }
- }
- transaction_chain_entry ch_e;
- ch_e.m_keeper_block_height = bl_height;
- ch_e.m_blob_size = blob_size;
- ch_e.tx = tx;
- auto i_r = m_transactions.insert(std::pair<crypto::hash, transaction_chain_entry>(tx_id, ch_e));
- if(!i_r.second)
- {
- LOG_PRINT_L1("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain");
- return false;
- }
- bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes);
- CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id);
- LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL
- << "tx_id: " << tx_id << ENDL
- << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")");
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_transactions.find(tx_id);
- if(it == m_transactions.end())
- {
- LOG_PRINT_RED_L1("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id);
- return false;
- }
-
- CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty");
- indexs = it->second.m_global_output_indexes;
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- bool res = check_tx_inputs(tx, &max_used_block_height);
- if(!res) return false;
- CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size());
- get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id);
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
-{
- BOOST_FOREACH(const txin_v& in, tx.vin)
- {
- CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true);
- if(have_tx_keyimg_as_spent(in_to_key.k_image))
- return true;
- }
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const
-{
- crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
- return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) const
-{
- size_t sig_index = 0;
- if(pmax_used_block_height)
- *pmax_used_block_height = 0;
-
- BOOST_FOREACH(const auto& txin, tx.vin)
- {
- CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs");
- const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
-
- CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx));
-
- if(have_tx_keyimg_as_spent(in_to_key.k_image))
- {
- LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image));
- return false;
- }
-
- CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
- if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height))
- {
- LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx));
- return false;
- }
-
- sig_index++;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const
-{
- if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
- {
- //interpret as block index
- if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time)
- return true;
- else
- return false;
- }else
- {
- //interpret as time
- uint64_t current_time = static_cast<uint64_t>(time(NULL));
- if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 >= unlock_time)
- return true;
- else
- return false;
- }
- return false;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
-{
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- struct outputs_visitor
- {
- std::vector<const crypto::public_key *>& m_results_collector;
- const blockchain_storage& m_bch;
- outputs_visitor(std::vector<const crypto::public_key *>& results_collector, const blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch)
- {}
- bool handle_output(const transaction& tx, const tx_out& out)
- {
- //check tx unlock time
- if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time))
- {
- LOG_PRINT_L1("One of outputs for one of inputs has wrong tx.unlock_time = " << tx.unlock_time);
- return false;
- }
-
- if(out.target.type() != typeid(txout_to_key))
- {
- LOG_PRINT_L1("Output has wrong type id, which=" << out.target.which());
- return false;
- }
-
- m_results_collector.push_back(&boost::get<txout_to_key>(out.target).key);
- return true;
- }
- };
-
- //check ring signature
- std::vector<const crypto::public_key *> output_keys;
- outputs_visitor vi(output_keys, *this);
- if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height))
- {
- LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
- return false;
- }
-
- if(txin.key_offsets.size() != output_keys.size())
- {
- LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
- return false;
- }
- CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
- if(m_is_in_checkpoint_zone)
- return true;
- return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data());
-}
-//------------------------------------------------------------------
-uint64_t blockchain_storage::get_adjusted_time() const
-{
- //TODO: add collecting median time
- return time(NULL);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp_main(const block& b) const
-{
- if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT)
- {
- LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours");
- return false;
- }
-
- std::vector<uint64_t> timestamps;
- size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
- for(;offset!= m_blocks.size(); ++offset)
- timestamps.push_back(m_blocks[offset].bl.timestamp);
-
- return check_block_timestamp(std::move(timestamps), b);
-}
-//------------------------------------------------------------------
-bool blockchain_storage::check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const
-{
- if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
- return true;
-
- uint64_t median_ts = epee::misc_utils::median(timestamps);
-
- if(b.timestamp < median_ts)
- {
- LOG_PRINT_L1("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts);
- return false;
- }
-
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
-{
- TIME_MEASURE_START(block_processing_time);
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- if(bl.prev_id != get_tail_id())
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "has wrong prev_id: " << bl.prev_id << ENDL
- << "expected: " << get_tail_id());
- return false;
- }
-
- if(!check_block_timestamp_main(bl))
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "has invalid timestamp: " << bl.timestamp);
- //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- //check proof of work
- TIME_MEASURE_START(target_calculating_time);
- difficulty_type current_diffic = get_difficulty_for_next_block();
- CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!");
- TIME_MEASURE_FINISH(target_calculating_time);
- TIME_MEASURE_START(longhash_calculating_time);
- crypto::hash proof_of_work = null_hash;
-
- // Formerly the code below contained an if loop with the following condition
- // !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())
- // however, this caused the daemon to not bother checking PoW for blocks
- // before checkpoints, which is very dangerous behaviour. We moved the PoW
- // validation out of the next chunk of code to make sure that we correctly
- // check PoW now.
- proof_of_work = get_block_longhash(bl, m_blocks.size());
-
- if(!check_hash(proof_of_work, current_diffic))
- {
- LOG_PRINT_L1("Block with id: " << id << ENDL
- << "does not have enough proof of work: " << proof_of_work << ENDL
- << "unexpected difficulty: " << current_diffic );
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- // If we're at a checkpoint, ensure that our hardcoded checkpoint hash
- // is correct.
- if(m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height()))
- {
- if(!m_checkpoints.check_block(get_current_blockchain_height(), id))
- {
- LOG_ERROR("CHECKPOINT VALIDATION FAILED");
- bvc.m_verifivation_failed = true;
- return false;
- }
- }
-
- TIME_MEASURE_FINISH(longhash_calculating_time);
-
- if(!prevalidate_miner_transaction(bl, m_blocks.size()))
- {
- LOG_PRINT_L1("Block with id: " << id
- << " failed to pass prevalidation");
- bvc.m_verifivation_failed = true;
- return false;
- }
- crypto::hash coinbase_hash = null_hash;
- size_t coinbase_blob_size = 0;
- get_transaction_hash(bl.miner_tx, coinbase_hash, coinbase_blob_size);
- size_t cumulative_block_size = coinbase_blob_size;
- //process transactions
- if(!add_transaction_from_block(bl.miner_tx, coinbase_hash, id, get_current_blockchain_height(), coinbase_blob_size))
- {
- LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage");
- bvc.m_verifivation_failed = true;
- return false;
- }
- size_t tx_processed_count = 0;
- uint64_t fee_summary = 0;
- BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes)
- {
- transaction tx;
- size_t blob_size = 0;
- uint64_t fee = 0;
- bool relayed = false;
- if(!m_tx_pool->take_tx(tx_id, tx, blob_size, fee, relayed))
- {
- LOG_PRINT_L1("Block with id: " << id << "has at least one unknown transaction with id: " << tx_id);
- purge_block_data_from_blockchain(bl, tx_processed_count);
- //add_block_as_invalid(bl, id);
- bvc.m_verifivation_failed = true;
- return false;
- }
- if(!check_tx_inputs(tx))
- {
- LOG_PRINT_L1("Block with id: " << id << "has at least one transaction (id: " << tx_id << ") with wrong inputs.");
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1);
- CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- add_block_as_invalid(bl, id);
- LOG_PRINT_L1("Block with id " << id << " added as invalid becouse of wrong inputs in transactions");
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height(), blob_size))
- {
- LOG_PRINT_L1("Block with id: " << id << " failed to add transaction to blockchain storage");
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- bool add_res = m_tx_pool->add_tx(tx, tvc, true, relayed, 1);
- CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
- fee_summary += fee;
- cumulative_block_size += blob_size;
- ++tx_processed_count;
- }
- uint64_t base_reward = 0;
- uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0;
- if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins))
- {
- LOG_PRINT_L1("Block with id: " << id
- << " has incorrect miner transaction");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
-
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = bl;
- bei.block_cumulative_size = cumulative_block_size;
- bei.cumulative_difficulty = current_diffic;
-
- // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of
- // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins
- // at MONEY_SUPPLY. already_generated_coins is only used to compute the block subsidy and MONEY_SUPPLY yields a
- // subsidy of 0 under the base formula and therefore the minimum subsidy >0 in the tail state.
-
- bei.already_generated_coins = base_reward < (MONEY_SUPPLY-already_generated_coins) ? already_generated_coins + base_reward : MONEY_SUPPLY;
-
- if(m_blocks.size())
- bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty;
-
- bei.height = m_blocks.size();
-
- auto ind_res = m_blocks_index.insert(std::pair<crypto::hash, size_t>(id, bei.height));
- if(!ind_res.second)
- {
- LOG_PRINT_L1("block with id: " << id << " already in block indexes");
- purge_block_data_from_blockchain(bl, tx_processed_count);
- bvc.m_verifivation_failed = true;
- return false;
- }
-
- m_blocks.push_back(bei);
- update_next_comulative_size_limit();
- TIME_MEASURE_FINISH(block_processing_time);
- LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id
- << ENDL << "PoW:\t" << proof_of_work
- << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic
- << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary)
- << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size
- << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms");
-
- epee::net_utils::data_logger::get_instance().add_data("blockchain_processing_time", block_processing_time);
-
- bvc.m_added_to_main_chain = true;
- /*if(!m_orphanes_reorganize_in_work)
- review_orphaned_blocks_with_new_block_id(id, true);*/
-
- m_tx_pool->on_blockchain_inc(bei.height, id);
- //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl));
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::update_next_comulative_size_limit()
-{
- std::vector<size_t> sz;
- get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
-
- uint64_t median = epee::misc_utils::median(sz);
- if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)
- median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
-
- m_current_block_cumul_sz_limit = median*2;
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc)
-{
- //copy block here to let modify block.target
- block bl = bl_;
- crypto::hash id = get_block_hash(bl);
- CRITICAL_REGION_LOCAL(*m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
- CRITICAL_REGION_LOCAL1(m_blockchain_lock);
- if(have_block(id))
- {
- LOG_PRINT_L3("block with id = " << id << " already exists");
- bvc.m_already_exists = true;
- return false;
- }
-
- //check that block refers to chain tail
- if(!(bl.prev_id == get_tail_id()))
- {
- //chain switching or wrong block
- bvc.m_added_to_main_chain = false;
- return handle_alternative_block(bl, id, bvc);
- //never relay alternative blocks
- }
-
- return handle_block_to_main_chain(bl, id, bvc);
-}
-//------------------------------------------------------------------
-void blockchain_storage::check_against_checkpoints(const checkpoints& points, bool enforce)
-{
- const auto& pts = points.get_points();
-
- for (const auto& pt : pts)
- {
- // if the checkpoint is for a block we don't have yet, move on
- if (pt.first >= m_blocks.size())
- {
- continue;
- }
-
- if (!points.check_block(pt.first, get_block_hash(m_blocks[pt.first].bl)))
- {
- // if asked to enforce checkpoints, roll back to a couple of blocks before the checkpoint
- if (enforce)
- {
- LOG_ERROR("Local blockchain failed to pass a checkpoint, rolling back!");
- std::list<block> empty;
- rollback_blockchain_switching(empty, pt.first - 2);
- }
- else
- {
- LOG_ERROR("WARNING: local blockchain failed to pass a MoneroPulse checkpoint, and you could be on a fork. You should either sync up from scratch, OR download a fresh blockchain bootstrap, OR enable checkpoint enforcing with the --enforce-dns-checkpointing command-line option");
- }
- }
- }
-}
-//------------------------------------------------------------------
-// returns false if any of the checkpoints loading returns false.
-// That should happen only if a checkpoint is added that conflicts
-// with an existing checkpoint.
-bool blockchain_storage::update_checkpoints(const std::string& file_path, bool check_dns)
-{
- if (!m_checkpoints.load_checkpoints_from_json(file_path))
- {
- return false;
- }
-
- // if we're checking both dns and json, load checkpoints from dns.
- // if we're not hard-enforcing dns checkpoints, handle accordingly
- if (m_enforce_dns_checkpoints && check_dns)
- {
- if (!m_checkpoints.load_checkpoints_from_dns())
- {
- return false;
- }
- }
- else if (check_dns)
- {
- checkpoints dns_points;
- dns_points.load_checkpoints_from_dns(m_testnet);
- if (m_checkpoints.check_for_conflicts(dns_points))
- {
- check_against_checkpoints(dns_points, false);
- }
- else
- {
- LOG_PRINT_L0("One or more checkpoints fetched from DNS conflicted with existing checkpoints!");
- }
- }
-
- check_against_checkpoints(m_checkpoints, true);
-
- return true;
-}
-//------------------------------------------------------------------
-void blockchain_storage::set_enforce_dns_checkpoints(bool enforce_checkpoints)
-{
- m_enforce_dns_checkpoints = enforce_checkpoints;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::flush_txes_from_pool(const std::list<crypto::hash> &txids)
-{
- bool res = true;
- for (const auto &txid: txids)
- {
- cryptonote::transaction tx;
- size_t blob_size;
- uint64_t fee;
- bool relayed;
- LOG_PRINT_L1("Removing txid " << txid << " from the pool");
- if(!m_tx_pool->take_tx(txid, tx, blob_size, fee, relayed))
- {
- LOG_PRINT_L0("Failed to remove txid " << txid << " from the pool");
- res = false;
- }
- }
- return res;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
-{
- for (key_images_container::const_iterator i = m_spent_keys.begin(); i != m_spent_keys.end(); ++i) {
- if (!f(*i))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const block&)> f) const
-{
- for (blocks_container::const_iterator i = m_blocks.begin(); i != m_blocks.end(); ++i) {
- crypto::hash hash;
- get_block_hash (i->bl, hash);
- if (!f(i->height, hash, i->bl))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_transactions(std::function<bool(const crypto::hash&, const transaction&)> f) const
-{
- for (transactions_container::const_iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) {
- if (!f(i->first, i->second.tx))
- return false;
- }
- return true;
-}
-//------------------------------------------------------------------
-bool blockchain_storage::for_all_outputs(std::function<bool(uint64_t, const crypto::hash&, size_t)> f) const
-{
- for (outputs_container::const_iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
- for (size_t n = 0; n < i->second.size(); ++n) {
- if (!f(i->first, i->second[n].first, i->second[n].second))
- return false;
- }
- }
- return true;
-}
diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h
deleted file mode 100644
index 878202cf1..000000000
--- a/src/cryptonote_core/blockchain_storage.h
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#pragma once
-#include <boost/serialization/serialization.hpp>
-#include <boost/serialization/version.hpp>
-#include <boost/serialization/list.hpp>
-#include <boost/multi_index_container.hpp>
-#include <boost/multi_index/global_fun.hpp>
-#include <boost/multi_index/hashed_index.hpp>
-#include <boost/multi_index/member.hpp>
-#include <boost/foreach.hpp>
-#include <atomic>
-
-#include "syncobj.h"
-#include "string_tools.h"
-#include "tx_pool.h"
-#include "cryptonote_basic.h"
-#include "common/util.h"
-#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "rpc/core_rpc_server_commands_defs.h"
-#include "difficulty.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "verification_context.h"
-#include "crypto/hash.h"
-#include "checkpoints.h"
-
-namespace cryptonote
-{
-
- /************************************************************************/
- /* */
- /************************************************************************/
- class blockchain_storage
- {
- public:
- struct transaction_chain_entry
- {
- transaction tx;
- uint64_t m_keeper_block_height;
- size_t m_blob_size;
- std::vector<uint64_t> m_global_output_indexes;
- };
-
- struct block_extended_info
- {
- block bl;
- uint64_t height;
- size_t block_cumulative_size;
- difficulty_type cumulative_difficulty;
- uint64_t already_generated_coins;
- };
-
- blockchain_storage(tx_memory_pool* tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false)
- {};
-
- bool init() { return init(tools::get_default_data_dir(), true); }
- bool init(const std::string& config_folder, bool testnet = false);
- bool deinit();
-
- void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
-
- //bool push_new_block();
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
- bool get_alternative_blocks(std::list<block>& blocks) const;
- size_t get_alternative_blocks_count() const;
- crypto::hash get_block_id_by_height(uint64_t height) const;
- bool get_block_by_hash(const crypto::hash &h, block &blk) const;
- void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const;
-
- template<class archive_t>
- void serialize(archive_t & ar, const unsigned int version);
-
- bool have_tx(const crypto::hash &id) const;
- bool have_tx_keyimges_as_spent(const transaction &tx) const;
- bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const;
- const transaction *get_tx(const crypto::hash &id) const;
-
- uint64_t get_current_blockchain_height() const;
- crypto::hash get_tail_id() const;
- crypto::hash get_tail_id(uint64_t& height) const;
- difficulty_type get_difficulty_for_next_block() const;
- bool add_new_block(const block& bl_, block_verification_context& bvc);
- bool reset_and_set_genesis_block(const block& b);
- bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
- bool have_block(const crypto::hash& id) const;
- size_t get_total_transactions() const;
- bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys) const;
- bool get_short_chain_history(std::list<crypto::hash>& ids) const;
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const;
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
- bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
- bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
- bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
- bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count) const;
- bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
- bool store_blockchain();
-
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id) const;
- uint64_t get_current_cumulative_blocksize_limit() const;
- bool is_storing_blockchain()const{return m_is_blockchain_storing;}
- uint64_t block_difficulty(size_t i) const;
- double get_avg_block_size( size_t count) const;
- bool flush_txes_from_pool(const std::list<crypto::hash> &txids);
-
- template<class t_ids_container, class t_blocks_container, class t_missed_container>
- bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& bl_id, block_ids)
- {
- auto it = m_blocks_index.find(bl_id);
- if(it == m_blocks_index.end())
- missed_bs.push_back(bl_id);
- else
- {
- CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id)
- << " have index record with offset="<<it->second<< ", bigger then m_blocks.size()=" << m_blocks.size());
- blocks.push_back(m_blocks[it->second].bl);
- }
- }
- return true;
- }
-
- template<class t_ids_container, class t_tx_container, class t_missed_container>
- bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
-
- BOOST_FOREACH(const auto& tx_id, txs_ids)
- {
- auto it = m_transactions.find(tx_id);
- if(it == m_transactions.end())
- {
- transaction tx;
- if(!m_tx_pool->get_transaction(tx_id, tx))
- missed_txs.push_back(tx_id);
- else
- txs.push_back(tx);
- }
- else
- txs.push_back(it->second.tx);
- }
- return true;
- }
- //debug functions
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
- void print_blockchain_index() const;
- void print_blockchain_outs(const std::string& file) const;
- void check_against_checkpoints(const checkpoints& points, bool enforce);
- bool update_checkpoints(const std::string& file_path, bool check_dns);
- void set_enforce_dns_checkpoints(bool enforce_checkpoints);
-
- block get_block(uint64_t height) const { return m_blocks[height].bl; }
- size_t get_block_size(uint64_t height) const { return m_blocks[height].block_cumulative_size; }
- difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; }
- uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; }
-
- bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
- bool for_all_blocks(std::function<bool(uint64_t height, const crypto::hash&, const block&)>) const;
- bool for_all_transactions(std::function<bool(const crypto::hash&, const transaction&)>) const;
- bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
-
- // use for testing only
- bool debug_pop_block_from_blockchain() { return pop_block_from_blockchain(); }
-
- private:
- typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
- typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
- typedef std::unordered_set<crypto::key_image> key_images_container;
- typedef std::vector<block_extended_info> blocks_container;
- typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
- typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
- typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
-
- tx_memory_pool* m_tx_pool;
- mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
-
- // main chain
- blocks_container m_blocks; // height -> block_extended_info
- blocks_by_id_index m_blocks_index; // crypto::hash -> height
- transactions_container m_transactions;
- key_images_container m_spent_keys;
- size_t m_current_block_cumul_sz_limit;
-
-
- // all alternative chains
- blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info
-
- // some invalid blocks
- blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
- outputs_container m_outputs;
-
-
- std::string m_config_folder;
- checkpoints m_checkpoints;
- std::atomic<bool> m_is_in_checkpoint_zone;
- std::atomic<bool> m_is_blockchain_storing;
-
- bool m_enforce_dns_checkpoints;
- bool m_testnet;
-
- // made private for consistency with blockchain.h
- template<class visitor_t>
- bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const;
- bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL) const;
- bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL) const;
- bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL) const;
-
- bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
- bool pop_block_from_blockchain();
- bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count);
- bool purge_transaction_from_blockchain(const crypto::hash& tx_id);
- bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check);
-
- bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
- bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
- bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
- bool prevalidate_miner_transaction(const block& b, uint64_t height) const;
- bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) const;
- bool validate_transaction(const block& b, uint64_t height, const transaction& tx) const;
- bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height);
- bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, size_t blob_size);
- bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
- bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
- bool get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const;
- bool add_out_to_get_random_outs(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
- bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
- bool add_block_as_invalid(const block& bl, const crypto::hash& h);
- bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
- size_t find_end_of_allowed_index(const std::vector<std::pair<crypto::hash, size_t> >& amount_outs) const;
- bool check_block_timestamp_main(const block& b) const;
- bool check_block_timestamp(std::vector<uint64_t> timestamps, const block& b) const;
- uint64_t get_adjusted_time() const;
- bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
- bool update_next_comulative_size_limit();
- bool store_genesis_block(bool testnet, bool check_added = false);
- };
-
-
- /************************************************************************/
- /* */
- /************************************************************************/
-
- #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12
-
- template<class archive_t>
- void blockchain_storage::serialize(archive_t & ar, const unsigned int version)
- {
- if(version < 11)
- return;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- ar & m_blocks;
- ar & m_blocks_index;
- ar & m_transactions;
- ar & m_spent_keys;
- ar & m_alternative_chains;
- ar & m_outputs;
- ar & m_invalid_blocks;
- ar & m_current_block_cumul_sz_limit;
- /*serialization bug workaround*/
- if(version > 11)
- {
- uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit;
- if(archive_t::is_saving::value)
- {
- ar & total_check_count;
- }else
- {
- uint64_t total_check_count_loaded = 0;
- ar & total_check_count_loaded;
- if(total_check_count != total_check_count_loaded)
- {
- LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count);
-
- LOG_PRINT_L0("Blockchain storage:" << ENDL <<
- "m_blocks: " << m_blocks.size() << ENDL <<
- "m_blocks_index: " << m_blocks_index.size() << ENDL <<
- "m_transactions: " << m_transactions.size() << ENDL <<
- "m_spent_keys: " << m_spent_keys.size() << ENDL <<
- "m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
- "m_outputs: " << m_outputs.size() << ENDL <<
- "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
- "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
-
- throw std::runtime_error("Blockchain data corruption");
- }
- }
- }
-
-
- LOG_PRINT_L2("Blockchain storage:" << ENDL <<
- "m_blocks: " << m_blocks.size() << ENDL <<
- "m_blocks_index: " << m_blocks_index.size() << ENDL <<
- "m_transactions: " << m_transactions.size() << ENDL <<
- "m_spent_keys: " << m_spent_keys.size() << ENDL <<
- "m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
- "m_outputs: " << m_outputs.size() << ENDL <<
- "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
- "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
- }
-
- //------------------------------------------------------------------
- template<class visitor_t>
- bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const
- {
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto it = m_outputs.find(tx_in_to_key.amount);
- if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size())
- return false;
-
- std::vector<uint64_t> absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets);
-
-
- const std::vector<std::pair<crypto::hash, size_t> >& amount_outs_vec = it->second;
- size_t count = 0;
- BOOST_FOREACH(uint64_t i, absolute_offsets)
- {
- if(i >= amount_outs_vec.size() )
- {
- LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1);
- return false;
- }
- transactions_container::const_iterator tx_it = m_transactions.find(amount_outs_vec[i].first);
- CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first));
- CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false,
- "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size());
- if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second]))
- {
- LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
- return false;
- }
- if(count++ == absolute_offsets.size()-1 && pmax_related_block_height)
- {
- if(*pmax_related_block_height < tx_it->second.m_keeper_block_height)
- *pmax_related_block_height = tx_it->second.m_keeper_block_height;
- }
- }
-
- return true;
- }
-}
-
-
-
-BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER)
diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h
index ed62e26f4..f54b8c2b3 100644
--- a/src/cryptonote_core/cryptonote_basic.h
+++ b/src/cryptonote_core/cryptonote_basic.h
@@ -49,9 +49,7 @@
#include "crypto/hash.h"
#include "misc_language.h"
#include "tx_extra.h"
-
-#define DB_MEMORY 1
-#define DB_LMDB 2
+#include "ringct/rctTypes.h"
namespace cryptonote
{
@@ -175,14 +173,14 @@ namespace cryptonote
BEGIN_SERIALIZE()
VARINT_FIELD(version)
- if(CURRENT_TRANSACTION_VERSION < version) return false;
+ if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
VARINT_FIELD(unlock_time)
FIELD(vin)
FIELD(vout)
FIELD(extra)
END_SERIALIZE()
- protected:
+ public:
transaction_prefix(){}
};
@@ -190,6 +188,7 @@ namespace cryptonote
{
public:
std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count
+ rct::rctSig rct_signatures;
transaction();
virtual ~transaction();
@@ -198,34 +197,59 @@ namespace cryptonote
BEGIN_SERIALIZE_OBJECT()
FIELDS(*static_cast<transaction_prefix *>(this))
- ar.tag("signatures");
- ar.begin_array();
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
- bool signatures_not_expected = signatures.empty();
- if (!signatures_not_expected && vin.size() != signatures.size())
- return false;
-
- for (size_t i = 0; i < vin.size(); ++i)
+ if (version == 1)
{
- size_t signature_size = get_signature_size(vin[i]);
- if (signatures_not_expected)
+ ar.tag("signatures");
+ ar.begin_array();
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
+ bool signatures_not_expected = signatures.empty();
+ if (!signatures_not_expected && vin.size() != signatures.size())
+ return false;
+
+ for (size_t i = 0; i < vin.size(); ++i)
{
- if (0 == signature_size)
- continue;
- else
+ size_t signature_size = get_signature_size(vin[i]);
+ if (signatures_not_expected)
+ {
+ if (0 == signature_size)
+ continue;
+ else
+ return false;
+ }
+
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
+ if (signature_size != signatures[i].size())
return false;
- }
-
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
- if (signature_size != signatures[i].size())
- return false;
- FIELDS(signatures[i]);
+ FIELDS(signatures[i]);
- if (vin.size() - i > 1)
- ar.delimit_array();
+ if (vin.size() - i > 1)
+ ar.delimit_array();
+ }
+ ar.end_array();
+ }
+ else
+ {
+ FIELD(rct_signatures)
+ switch (rct_signatures.type)
+ {
+ case rct::RCTTypeNull:
+ break;
+ case rct::RCTTypeSimple:
+ if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size())
+ return false;
+ break;
+ case rct::RCTTypeFull:
+ for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i)
+ {
+ if (rct_signatures.mixRing[i].size() != vin.size())
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
}
- ar.end_array();
END_SERIALIZE()
private:
@@ -248,12 +272,13 @@ namespace cryptonote
inline
void transaction::set_null()
{
- version = 0;
+ version = 1;
unlock_time = 0;
vin.clear();
vout.clear();
extra.clear();
signatures.clear();
+ rct_signatures.type = rct::RCTTypeNull;
}
inline
diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h
index 79ceec5bc..c91f78c58 100644
--- a/src/cryptonote_core/cryptonote_boost_serialization.h
+++ b/src/cryptonote_core/cryptonote_boost_serialization.h
@@ -37,9 +37,13 @@
#include <boost/serialization/map.hpp>
#include <boost/foreach.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/archive/binary_oarchive.hpp>
#include "cryptonote_basic.h"
#include "common/unordered_containers_boost_serialization.h"
#include "crypto/crypto.h"
+#include "ringct/rctTypes.h"
+#include "ringct/rctOps.h"
//namespace cryptonote {
namespace boost
@@ -140,6 +144,16 @@ namespace boost
template <class Archive>
+ inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
+ {
+ a & x.version;
+ a & x.unlock_time;
+ a & x.vin;
+ a & x.vout;
+ a & x.extra;
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver)
{
a & x.version;
@@ -147,7 +161,10 @@ namespace boost
a & x.vin;
a & x.vout;
a & x.extra;
- a & x.signatures;
+ if (x.version == 1)
+ a & x.signatures;
+ else
+ a & x.rct_signatures;
}
@@ -163,6 +180,107 @@ namespace boost
a & b.miner_tx;
a & b.tx_hashes;
}
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::key &x, const boost::serialization::version_type ver)
+ {
+ a & reinterpret_cast<char (&)[sizeof(rct::key)]>(x);
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::ctkey &x, const boost::serialization::version_type ver)
+ {
+ a & x.dest;
+ a & x.mask;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rangeSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.asig;
+ a & x.Ci;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::asnlSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.L1;
+ a & x.s2;
+ a & x.s;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.ss;
+ a & x.cc;
+ // a & x.II; // not serialized, we can recover it from the tx vin
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver)
+ {
+ a & x.mask;
+ a & x.amount;
+ // a & x.senderPk; // not serialized, as we do not use it in monero currently
+ }
+
+ inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver)
+ {
+ rct::keyV outPk;
+ a & outPk;
+ outPk_.resize(outPk.size());
+ for (size_t n = 0; n < outPk_.size(); ++n)
+ {
+ outPk_[n].dest = rct::identity();
+ outPk_[n].mask = outPk[n];
+ }
+ }
+
+ inline void serializeOutPk(boost::archive::binary_oarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver)
+ {
+ rct::keyV outPk(outPk_.size());
+ for (size_t n = 0; n < outPk_.size(); ++n)
+ outPk[n] = outPk_[n].mask;
+ a & outPk;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver)
+ {
+ a & x.type;
+ if (x.type == rct::RCTTypeNull)
+ return;
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
+ // a & x.message; message is not serialized, as it can be reconstructed from the tx data
+ // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
+ if (x.type == rct::RCTTypeSimple)
+ a & x.pseudoOuts;
+ a & x.ecdhInfo;
+ serializeOutPk(a, x.outPk, ver);
+ a & x.txnFee;
+ }
+
+ template <class Archive>
+ inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
+ {
+ a & x.type;
+ if (x.type == rct::RCTTypeNull)
+ return;
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
+ // a & x.message; message is not serialized, as it can be reconstructed from the tx data
+ // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
+ if (x.type == rct::RCTTypeSimple)
+ a & x.pseudoOuts;
+ a & x.ecdhInfo;
+ serializeOutPk(a, x.outPk, ver);
+ a & x.txnFee;
+ //--------------
+ a & x.p.rangeSigs;
+ a & x.p.MGs;
+ }
}
}
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 26748aceb..511f50616 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -43,6 +43,7 @@ using namespace epee;
#include "misc_language.h"
#include <csignal>
#include "cryptonote_core/checkpoints.h"
+#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#if defined(BERKELEY_DB)
@@ -57,11 +58,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage(m_mempool),
-#else
- m_blockchain_storage(&m_mempool),
-#endif
m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),
@@ -257,7 +254,6 @@ namespace cryptonote
r = m_mempool.init(m_fakechain ? std::string() : m_config_folder);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
-#if BLOCKCHAIN_DB == DB_LMDB
std::string db_type = command_line::get_arg(vm, command_line::arg_db_type);
std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode);
bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
@@ -405,9 +401,6 @@ namespace cryptonote
bool show_time_stats = command_line::get_arg(vm, command_line::arg_show_time_stats) != 0;
m_blockchain_storage.set_show_time_stats(show_time_stats);
-#else
- r = m_blockchain_storage.init(m_config_folder, m_testnet);
-#endif
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
// load json & DNS checkpoints, and verify them
@@ -505,6 +498,15 @@ namespace cryptonote
}
//std::cout << "!"<< tx.vin.size() << std::endl;
+ uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
+ const size_t max_tx_version = version == 1 ? 1 : 2;
+ if (tx.version == 0 || tx.version > max_tx_version)
+ {
+ // v2 is the latest one we know
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+
if(!check_tx_syntax(tx))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected");
@@ -560,6 +562,14 @@ namespace cryptonote
LOG_PRINT_RED_L1("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
return false;
}
+ if (tx.version > 1)
+ {
+ if (tx.rct_signatures.outPk.size() != tx.vout.size())
+ {
+ LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx));
+ return false;
+ }
+ }
if(!check_money_overflow(tx))
{
@@ -567,15 +577,19 @@ namespace cryptonote
return false;
}
- uint64_t amount_in = 0;
- get_inputs_money_amount(tx, amount_in);
- uint64_t amount_out = get_outs_money_amount(tx);
-
- if(amount_in <= amount_out)
+ if (tx.version == 1)
{
- LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
- return false;
+ uint64_t amount_in = 0;
+ get_inputs_money_amount(tx, amount_in);
+ uint64_t amount_out = get_outs_money_amount(tx);
+
+ if(amount_in <= amount_out)
+ {
+ LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
+ return false;
+ }
}
+ // for version > 1, ringct signatures check verifies amounts match
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
{
@@ -714,6 +728,11 @@ namespace cryptonote
return m_blockchain_storage.get_outs(req, res);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const
+ {
+ return m_blockchain_storage.get_random_rct_outs(req, res);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const
{
return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs);
@@ -780,18 +799,14 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks)
{
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage.prepare_handle_incoming_blocks(blocks);
-#endif
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::cleanup_handle_incoming_blocks(bool force_sync)
{
-#if BLOCKCHAIN_DB == DB_LMDB
m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync);
-#endif
return true;
}
@@ -918,11 +933,6 @@ namespace cryptonote
m_starter_message_showed = true;
}
-#if BLOCKCHAIN_DB == DB_LMDB
- // m_store_blockchain_interval.do_call(boost::bind(&Blockchain::store_blockchain, &m_blockchain_storage));
-#else
- m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage));
-#endif
m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this));
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_miner.on_idle();
@@ -932,7 +942,6 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_fork_time()
{
-#if BLOCKCHAIN_DB == DB_LMDB
HardFork::State state = m_blockchain_storage.get_hard_fork_state();
switch (state) {
case HardFork::LikelyForked:
@@ -951,7 +960,6 @@ namespace cryptonote
default:
break;
}
-#endif
return true;
}
//-----------------------------------------------------------------------------------------------
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 63969bc23..d16bd6553 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -40,11 +40,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "tx_pool.h"
-#if BLOCKCHAIN_DB == DB_LMDB
#include "blockchain.h"
-#else
-#include "blockchain_storage.h"
-#endif
#include "miner.h"
#include "connection_context.h"
#include "cryptonote_core/cryptonote_stat_info.h"
@@ -483,6 +479,14 @@ namespace cryptonote
*/
bool get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) const;
+ /**
+ *
+ * @copydoc Blockchain::get_random_rct_outs
+ *
+ * @note see Blockchain::get_random_rct_outs
+ */
+ bool get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) const;
+
/**
* @copydoc miner::pause
@@ -498,7 +502,6 @@ namespace cryptonote
*/
void resume_mine();
-#if BLOCKCHAIN_DB == DB_LMDB
/**
* @brief gets the Blockchain instance
*
@@ -512,10 +515,6 @@ namespace cryptonote
* @return a const reference to the Blockchain instance
*/
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
-#else
- blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;}
- const blockchain_storage& get_blockchain_storage()const{return m_blockchain_storage;}
-#endif
/**
* @copydoc Blockchain::print_blockchain
@@ -765,11 +764,7 @@ namespace cryptonote
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
tx_memory_pool m_mempool; //!< transaction pool instance
-#if BLOCKCHAIN_DB == DB_LMDB
Blockchain m_blockchain_storage; //!< Blockchain instance
-#else
- blockchain_storage m_blockchain_storage;
-#endif
i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index ff752ae47..32b96f1fc 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -37,6 +37,7 @@ using namespace epee;
#include "miner.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#include "ringct/rctSigs.h"
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
@@ -100,7 +101,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
//TODO: validate tx
- crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash);
+ get_transaction_hash(tx, tx_hash);
get_transaction_prefix_hash(tx, tx_prefix_hash);
return true;
}
@@ -135,7 +136,10 @@ namespace cryptonote
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
// emission schedule
- if (hard_fork_version >= 2) {
+ // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
+ // and avoids the quantization. These outputs will be added as rct outputs with identity
+ // masks, to they can be used as rct inputs.
+ if (hard_fork_version >= 2 && hard_fork_version < 4) {
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
}
@@ -145,12 +149,16 @@ namespace cryptonote
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
- if (height == 0)
+ if (height == 0 || hard_fork_version >= 4)
{
// the genesis block was not decomposed, for unknown reasons
while (max_outs < out_amounts.size())
{
- out_amounts[out_amounts.size() - 2] += out_amounts.back();
+ //out_amounts[out_amounts.size() - 2] += out_amounts.back();
+ //out_amounts.resize(out_amounts.size() - 1);
+ out_amounts[1] += out_amounts[0];
+ for (size_t n = 1; n < out_amounts.size(); ++n)
+ out_amounts[n - 1] = out_amounts[n];
out_amounts.resize(out_amounts.size() - 1);
}
}
@@ -181,7 +189,11 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
- tx.version = CURRENT_TRANSACTION_VERSION;
+ if (hard_fork_version >= 4)
+ tx.version = 2;
+ else
+ tx.version = 1;
+
//lock
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
tx.vin.push_back(in);
@@ -252,6 +264,11 @@ namespace cryptonote
//---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee)
{
+ if (tx.version > 1)
+ {
+ fee = tx.rct_signatures.txnFee;
+ return true;
+ }
uint64_t amount_in = 0;
uint64_t amount_out = 0;
BOOST_FOREACH(auto& in, tx.vin)
@@ -315,6 +332,11 @@ namespace cryptonote
return pub_key_field.pub_key;
}
//---------------------------------------------------------------
+ crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx_prefix)
+ {
+ return get_tx_pub_key_from_extra(tx_prefix.extra);
+ }
+ //---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx)
{
return get_tx_pub_key_from_extra(tx.extra);
@@ -447,13 +469,16 @@ namespace cryptonote
return encrypt_payment_id(payment_id, public_key, secret_key);
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
{
+ std::vector<crypto::secret_key> amount_keys;
tx.vin.clear();
tx.vout.clear();
tx.signatures.clear();
+ tx.rct_signatures = rct::rctSig();
+ amount_keys.clear();
- tx.version = CURRENT_TRANSACTION_VERSION;
+ tx.version = rct ? 2 : 1;
tx.unlock_time = unlock_time;
tx.extra = extra;
@@ -509,7 +534,6 @@ namespace cryptonote
};
std::vector<input_generation_context_data> in_contexts;
-
uint64_t summary_inputs_money = 0;
//fill inputs
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
@@ -529,7 +553,7 @@ namespace cryptonote
return false;
//check that derivated key is equal with real output key
- if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) )
+ if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
{
LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
@@ -559,12 +583,18 @@ namespace cryptonote
size_t output_index = 0;
BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts)
{
- CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount);
+ CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
crypto::public_key out_eph_public_key;
bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
+ if (tx.version > 1)
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, output_index, scalar1);
+ amount_keys.push_back(scalar1);
+ }
r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
@@ -586,33 +616,148 @@ namespace cryptonote
}
- //generate ring signatures
- crypto::hash tx_prefix_hash;
- get_transaction_prefix_hash(tx, tx_prefix_hash);
-
- std::stringstream ss_ring_s;
- size_t i = 0;
- BOOST_FOREACH(const tx_source_entry& src_entr, sources)
+ if (tx.version == 1)
{
- ss_ring_s << "pub_keys:" << ENDL;
- std::vector<const crypto::public_key*> keys_ptrs;
- BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
+ //generate ring signatures
+ crypto::hash tx_prefix_hash;
+ get_transaction_prefix_hash(tx, tx_prefix_hash);
+
+ std::stringstream ss_ring_s;
+ size_t i = 0;
+ BOOST_FOREACH(const tx_source_entry& src_entr, sources)
{
- keys_ptrs.push_back(&o.second);
- ss_ring_s << o.second << ENDL;
+ ss_ring_s << "pub_keys:" << ENDL;
+ std::vector<const crypto::public_key*> keys_ptrs;
+ std::vector<crypto::public_key> keys(src_entr.outputs.size());
+ size_t ii = 0;
+ BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
+ {
+ keys[ii] = rct2pk(o.second.dest);
+ keys_ptrs.push_back(&keys[ii]);
+ ss_ring_s << o.second.dest << ENDL;
+ ++ii;
+ }
+
+ tx.signatures.push_back(std::vector<crypto::signature>());
+ std::vector<crypto::signature>& sigs = tx.signatures.back();
+ sigs.resize(src_entr.outputs.size());
+ crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
+ ss_ring_s << "signatures:" << ENDL;
+ std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
+ ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output;
+ i++;
}
- tx.signatures.push_back(std::vector<crypto::signature>());
- std::vector<crypto::signature>& sigs = tx.signatures.back();
- sigs.resize(src_entr.outputs.size());
- crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
- ss_ring_s << "signatures:" << ENDL;
- std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
- ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output;
- i++;
+ LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3);
}
+ else
+ {
+ bool all_rct_inputs = true;
+ size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
+ BOOST_FOREACH(const tx_source_entry& src_entr, sources)
+ all_rct_inputs &= !(src_entr.mask == rct::identity());
- LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3);
+ // the non-simple version is slightly smaller, but assumes all real inputs
+ // are on the same index, so can only be used if there just one ring.
+ bool use_simple_rct = sources.size() > 1;
+
+ if (!use_simple_rct)
+ {
+ // non simple ringct requires all real inputs to be at the same index for all inputs
+ BOOST_FOREACH(const tx_source_entry& src_entr, sources)
+ {
+ if(src_entr.real_output != sources.begin()->real_output)
+ {
+ LOG_ERROR("All inputs must have the same index for non-simple ringct");
+ return false;
+ }
+ }
+
+ // enforce same mixin for all outputs
+ for (size_t i = 1; i < sources.size(); ++i) {
+ if (n_total_outs != sources[i].outputs.size()) {
+ LOG_ERROR("Non-simple ringct transaction has varying mixin");
+ return false;
+ }
+ }
+ }
+
+ uint64_t amount_in = 0, amount_out = 0;
+ rct::ctkeyV inSk;
+ // mixRing indexing is done the other way round for simple
+ rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
+ rct::keyV destinations;
+ std::vector<uint64_t> inamounts, outamounts;
+ std::vector<unsigned int> index;
+ for (size_t i = 0; i < sources.size(); ++i)
+ {
+ rct::ctkey ctkey;
+ amount_in += sources[i].amount;
+ inamounts.push_back(sources[i].amount);
+ index.push_back(sources[i].real_output);
+ // inSk: (secret key, mask)
+ ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
+ ctkey.mask = sources[i].mask;
+ inSk.push_back(ctkey);
+ // inPk: (public key, commitment)
+ // will be done when filling in mixRing
+ }
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ {
+ destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
+ outamounts.push_back(tx.vout[i].amount);
+ amount_out += tx.vout[i].amount;
+ }
+
+ if (use_simple_rct)
+ {
+ // mixRing indexing is done the other way round for simple
+ for (size_t i = 0; i < sources.size(); ++i)
+ {
+ mixRing[i].resize(sources[i].outputs.size());
+ for (size_t n = 0; n < sources[i].outputs.size(); ++n)
+ {
+ mixRing[i][n] = sources[i].outputs[n].second;
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
+ {
+ mixRing[i].resize(sources.size());
+ for (size_t n = 0; n < sources.size(); ++n)
+ {
+ mixRing[i][n] = sources[n].outputs[i].second;
+ }
+ }
+ }
+
+ // fee
+ if (!use_simple_rct && amount_in > amount_out)
+ outamounts.push_back(amount_in - amount_out);
+
+ // zero out all amounts to mask rct outputs, real amounts are now encrypted
+ for (size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ if (sources[i].rct)
+ boost::get<txin_to_key>(tx.vin[i]).amount = 0;
+ }
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ tx.vout[i].amount = 0;
+
+ crypto::hash tx_prefix_hash;
+ get_transaction_prefix_hash(tx, tx_prefix_hash);
+ rct::ctkeyV outSk;
+ if (use_simple_rct)
+ tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, (const rct::keyV&)amount_keys, index, outSk);
+ else
+ tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, (const rct::keyV&)amount_keys, sources[0].real_output, outSk); // same index assumption
+
+ CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
+
+ LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3);
+ }
return true;
}
@@ -661,7 +806,10 @@ namespace cryptonote
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
<< ", in transaction id=" << get_transaction_hash(tx));
- CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx));
+ if (tx.version == 1)
+ {
+ CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx));
+ }
if(!check_key(boost::get<txout_to_key>(out.target).key))
return false;
@@ -776,20 +924,49 @@ namespace cryptonote
crypto::hash get_transaction_hash(const transaction& t)
{
crypto::hash h = null_hash;
- size_t blob_size = 0;
- get_object_hash(t, h, blob_size);
+ get_transaction_hash(t, h, NULL);
return h;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res)
{
- size_t blob_size = 0;
- return get_object_hash(t, res, blob_size);
+ return get_transaction_hash(t, res, NULL);
+ }
+ //---------------------------------------------------------------
+ bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
+ {
+ // v1 transactions hash the entire blob
+ if (t.version == 1)
+ {
+ size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size;
+ return get_object_hash(t, res, blob_size_ref);
+ }
+
+ // v2 transactions hash different parts together, than hash the set of those hashes
+ crypto::hash hashes[3];
+
+ // prefix
+ get_transaction_prefix_hash(t, hashes[0]);
+
+ // base rct data
+ get_blob_hash(t_serializable_object_to_blob((const rct::rctSigBase&)t.rct_signatures), hashes[1]);
+
+ // prunable rct data
+ get_blob_hash(t_serializable_object_to_blob(t.rct_signatures.p), hashes[2]);
+
+ // the tx hash is the hash of the 3 hashes
+ res = cn_fast_hash(hashes, sizeof(hashes));
+
+ // we still need the size
+ if (blob_size)
+ *blob_size = get_object_blobsize(t);
+
+ return true;
}
//---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size)
{
- return get_object_hash(t, res, blob_size);
+ return get_transaction_hash(t, res, &blob_size);
}
//---------------------------------------------------------------
blobdata get_block_hashing_blob(const block& b)
diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h
index 056940e98..e6a3bfba4 100644
--- a/src/cryptonote_core/cryptonote_format_utils.h
+++ b/src/cryptonote_core/cryptonote_format_utils.h
@@ -35,6 +35,7 @@
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#include "ringct/rctOps.h"
namespace cryptonote
@@ -50,13 +51,17 @@ namespace cryptonote
struct tx_source_entry
{
- typedef std::pair<uint64_t, crypto::public_key> output_entry;
+ typedef std::pair<uint64_t, rct::ctkey> output_entry;
- std::vector<output_entry> outputs; //index + key
+ std::vector<output_entry> outputs; //index + key + optional ringct commitment
size_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //incoming real tx public key
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
+ bool rct; //true if the output is rct
+ rct::key mask; //ringct amount mask
+
+ void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
};
struct tx_destination_entry
@@ -70,7 +75,7 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
template<typename T>
bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field)
@@ -85,6 +90,7 @@ namespace cryptonote
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra);
+ crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx);
bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
@@ -106,6 +112,7 @@ namespace cryptonote
crypto::hash get_transaction_hash(const transaction& t);
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size);
+ bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
blobdata get_block_hashing_blob(const block& b);
bool get_block_hash(const block& b, crypto::hash& res);
crypto::hash get_block_hash(const block& b);
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 3d5ab86e1..46fab4dcf 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -37,11 +37,7 @@
#include "cryptonote_format_utils.h"
#include "cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
-#if BLOCKCHAIN_DB == DB_LMDB
#include "blockchain.h"
-#else
-#include "blockchain_storage.h"
-#endif
#include "common/boost_serialization_helper.h"
#include "common/int-util.h"
#include "misc_language.h"
@@ -74,21 +70,22 @@ namespace cryptonote
}
}
//---------------------------------------------------------------------------------
-#if BLOCKCHAIN_DB == DB_LMDB
//---------------------------------------------------------------------------------
tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs)
{
}
-#else
- tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs)
- {
-
- }
-#endif
//---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version)
{
+ if (tx.version == 0)
+ {
+ // v0 never accepted
+ LOG_PRINT_L1("transaction version 0 is invalid");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+
// we do not accept transactions that timed out before, unless they're
// kept_by_block
if (!kept_by_block && m_timed_out_transactions.find(id) != m_timed_out_transactions.end())
@@ -106,25 +103,34 @@ namespace cryptonote
return false;
}
- uint64_t inputs_amount = 0;
- if(!get_inputs_money_amount(tx, inputs_amount))
+ // fee per kilobyte, size rounded up.
+ uint64_t fee;
+
+ if (tx.version == 1)
{
- tvc.m_verifivation_failed = true;
- return false;
- }
+ uint64_t inputs_amount = 0;
+ if(!get_inputs_money_amount(tx, inputs_amount))
+ {
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
- uint64_t outputs_amount = get_outs_money_amount(tx);
+ uint64_t outputs_amount = get_outs_money_amount(tx);
+ if(outputs_amount >= inputs_amount)
+ {
+ LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
+ tvc.m_verifivation_failed = true;
+ tvc.m_overspend = true;
+ return false;
+ }
- if(outputs_amount >= inputs_amount)
+ fee = inputs_amount - outputs_amount;
+ }
+ else
{
- LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
- tvc.m_verifivation_failed = true;
- tvc.m_overspend = true;
- return false;
+ fee = tx.rct_signatures.txnFee;
}
- // fee per kilobyte, size rounded up.
- uint64_t fee = inputs_amount - outputs_amount;
uint64_t needed_fee = blob_size / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0;
needed_fee *= FEE_PER_KB;
@@ -161,7 +167,7 @@ namespace cryptonote
if (!m_blockchain.check_tx_outputs(tx, tvc))
{
- LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout");
+ LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid output");
tvc.m_verifivation_failed = true;
tvc.m_invalid_output = true;
return false;
@@ -169,11 +175,9 @@ namespace cryptonote
crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0;
-#if BLOCKCHAIN_DB == DB_LMDB
- bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
-#else
- bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id);
-#endif
+ tx_details txd;
+ txd.tx = tx;
+ bool ch_inp_res = m_blockchain.check_tx_inputs(txd.tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
CRITICAL_REGION_LOCAL(m_transactions_lock);
if(!ch_inp_res)
{
@@ -181,11 +185,10 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check.
if(kept_by_block)
{
- auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
+ auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
txd_p.first->second.blob_size = blob_size;
- txd_p.first->second.tx = tx;
- txd_p.first->second.fee = inputs_amount - outputs_amount;
+ txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = null_hash;
txd_p.first->second.max_used_block_height = 0;
txd_p.first->second.kept_by_block = kept_by_block;
@@ -203,12 +206,11 @@ namespace cryptonote
}else
{
//update transactions container
- auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
+ auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd));
CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool");
txd_p.first->second.blob_size = blob_size;
- txd_p.first->second.tx = tx;
txd_p.first->second.kept_by_block = kept_by_block;
- txd_p.first->second.fee = inputs_amount - outputs_amount;
+ txd_p.first->second.fee = fee;
txd_p.first->second.max_used_block_id = max_used_block_id;
txd_p.first->second.max_used_block_height = max_used_block_height;
txd_p.first->second.last_failed_height = 0;
@@ -518,12 +520,8 @@ namespace cryptonote
if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
return false;
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
-#if BLOCKCHAIN_DB == DB_LMDB
tx_verification_context tvc;
if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
-#else
- if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
-#endif
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -649,6 +647,7 @@ namespace cryptonote
//---------------------------------------------------------------------------------
size_t tx_memory_pool::validate(uint8_t version)
{
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
size_t n_removed = 0;
size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2);
for (auto it = m_transactions.begin(); it != m_transactions.end(); ) {
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index c7aab7f08..0e280872d 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -48,11 +48,7 @@
namespace cryptonote
{
-#if BLOCKCHAIN_DB == DB_LMDB
class Blockchain;
-#else
- class blockchain_storage;
-#endif
/************************************************************************/
/* */
/************************************************************************/
@@ -93,16 +89,12 @@ namespace cryptonote
class tx_memory_pool: boost::noncopyable
{
public:
-#if BLOCKCHAIN_DB == DB_LMDB
/**
* @brief Constructor
*
* @param bchs a Blockchain class instance, for getting chain info
*/
tx_memory_pool(Blockchain& bchs);
-#else
- tx_memory_pool(blockchain_storage& bchs);
-#endif
/**
@@ -492,18 +484,7 @@ namespace cryptonote
std::unordered_set<crypto::hash> m_timed_out_transactions;
std::string m_config_folder; //!< the folder to save state to
-#if BLOCKCHAIN_DB == DB_LMDB
Blockchain& m_blockchain; //!< reference to the Blockchain object
-#else
- blockchain_storage& m_blockchain;
-#endif
-
-#if BLOCKCHAIN_DB == DB_LMDB
-#else
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- friend class blockchain_storage;
-#endif
-#endif
};
}
diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h
index e58291ea9..0bb635e84 100644
--- a/src/cryptonote_core/verification_context.h
+++ b/src/cryptonote_core/verification_context.h
@@ -47,6 +47,7 @@ namespace cryptonote
bool m_too_big;
bool m_overspend;
bool m_fee_too_low;
+ bool m_not_rct;
};
struct block_verification_context
diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt
new file mode 100644
index 000000000..078199bb0
--- /dev/null
+++ b/src/ringct/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (c) 2016, The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set(ringct_sources
+ rctOps.cpp
+ rctSigs.cpp
+ rctTypes.cpp
+ rctCryptoOps.c)
+
+set(ringct_headers)
+
+set(ringct_private_headers
+ rctOps.h
+ rctSigs.h
+ rctTypes.h)
+
+bitmonero_private_headers(ringct
+ ${crypto_private_headers})
+bitmonero_add_library(ringct
+ ${ringct_sources}
+ ${ringct_headers}
+ ${ringct_private_headers})
+target_link_libraries(ringct
+ LINK_PUBLIC
+ common
+ crypto
+ ${Boost_DATE_TIME_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
+ LINK_PRIVATE
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${EXTRA_LIBRARIES})
diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c
new file mode 100644
index 000000000..9bb9a6891
--- /dev/null
+++ b/src/ringct/rctCryptoOps.c
@@ -0,0 +1,221 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "crypto/crypto-ops.h"
+
+//DISABLE_VS_WARNINGS(4146 4244)
+
+void sc_reduce32copy(unsigned char * scopy, const unsigned char *s) {
+ int64_t s0 = 2097151 & load_3(s);
+ int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
+ int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
+ int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
+ int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
+ int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
+ int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
+ int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
+ int64_t s8 = 2097151 & load_3(s + 21);
+ int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
+ int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
+ int64_t s11 = (load_4(s + 28) >> 7);
+ int64_t s12 = 0;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+
+ carry0 = (s0 + (1<<20)) >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry2 = (s2 + (1<<20)) >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry4 = (s4 + (1<<20)) >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry6 = (s6 + (1<<20)) >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+
+ carry1 = (s1 + (1<<20)) >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry3 = (s3 + (1<<20)) >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry5 = (s5 + (1<<20)) >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry7 = (s7 + (1<<20)) >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+ carry11 = s11 >> 21;
+ s12 += carry11;
+ s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+
+ carry0 = s0 >> 21;
+ s1 += carry0;
+ s0 -= carry0 << 21;
+ carry1 = s1 >> 21;
+ s2 += carry1;
+ s1 -= carry1 << 21;
+ carry2 = s2 >> 21;
+ s3 += carry2;
+ s2 -= carry2 << 21;
+ carry3 = s3 >> 21;
+ s4 += carry3;
+ s3 -= carry3 << 21;
+ carry4 = s4 >> 21;
+ s5 += carry4;
+ s4 -= carry4 << 21;
+ carry5 = s5 >> 21;
+ s6 += carry5;
+ s5 -= carry5 << 21;
+ carry6 = s6 >> 21;
+ s7 += carry6;
+ s6 -= carry6 << 21;
+ carry7 = s7 >> 21;
+ s8 += carry7;
+ s7 -= carry7 << 21;
+ carry8 = s8 >> 21;
+ s9 += carry8;
+ s8 -= carry8 << 21;
+ carry9 = s9 >> 21;
+ s10 += carry9;
+ s9 -= carry9 << 21;
+ carry10 = s10 >> 21;
+ s11 += carry10;
+ s10 -= carry10 << 21;
+
+ scopy[0] = s0 >> 0;
+ scopy[1] = s0 >> 8;
+ scopy[2] = (s0 >> 16) | (s1 << 5);
+ scopy[3] = s1 >> 3;
+ scopy[4] = s1 >> 11;
+ scopy[5] = (s1 >> 19) | (s2 << 2);
+ scopy[6] = s2 >> 6;
+ scopy[7] = (s2 >> 14) | (s3 << 7);
+ scopy[8] = s3 >> 1;
+ scopy[9] = s3 >> 9;
+ scopy[10] = (s3 >> 17) | (s4 << 4);
+ scopy[11] = s4 >> 4;
+ scopy[12] = s4 >> 12;
+ scopy[13] = (s4 >> 20) | (s5 << 1);
+ scopy[14] = s5 >> 7;
+ scopy[15] = (s5 >> 15) | (s6 << 6);
+ scopy[16] = s6 >> 2;
+ scopy[17] = s6 >> 10;
+ scopy[18] = (s6 >> 18) | (s7 << 3);
+ scopy[19] = s7 >> 5;
+ scopy[20] = s7 >> 13;
+ scopy[21] = s8 >> 0;
+ scopy[22] = s8 >> 8;
+ scopy[23] = (s8 >> 16) | (s9 << 5);
+ scopy[24] = s9 >> 3;
+ scopy[25] = s9 >> 11;
+ scopy[26] = (s9 >> 19) | (s10 << 2);
+ scopy[27] = s10 >> 6;
+ scopy[28] = (s10 >> 14) | (s11 << 7);
+ scopy[29] = s11 >> 1;
+ scopy[30] = s11 >> 9;
+ scopy[31] = s11 >> 17;
+}
diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h
new file mode 100644
index 000000000..58c6964d8
--- /dev/null
+++ b/src/ringct/rctCryptoOps.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+extern "C" {
+#include "crypto/crypto-ops.h"
+}
+
+void sc_reduce32copy(unsigned char * scopy, const unsigned char *s);
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
new file mode 100644
index 000000000..488e47ca0
--- /dev/null
+++ b/src/ringct/rctOps.cpp
@@ -0,0 +1,475 @@
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "misc_log_ex.h"
+#include "rctOps.h"
+using namespace crypto;
+using namespace std;
+
+namespace rct {
+
+ //Various key initialization functions
+
+ //Creates a zero scalar
+ void zero(key &zero) {
+ memset(&zero, 0, 32);
+ }
+
+ //Creates a zero scalar
+ key zero() {
+ static const key z = { {0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
+ return z;
+ }
+
+ //Creates a zero elliptic curve point
+ void identity(key &Id) {
+ Id[0] = (unsigned char)(0x01);
+ memset(Id.bytes+1, 0, 31);
+ }
+
+ //Creates a zero elliptic curve point
+ key identity() {
+ key Id;
+ Id[0] = (unsigned char)(0x01);
+ memset(Id.bytes+1, 0, 31);
+ return Id;
+ }
+
+ //copies a scalar or point
+ void copy(key &AA, const key &A) {
+ memcpy(&AA, &A, 32);
+ }
+
+ //copies a scalar or point
+ key copy(const key &A) {
+ key AA;
+ memcpy(&AA, &A, 32);
+ return AA;
+ }
+
+
+ //initializes a key matrix;
+ //first parameter is rows,
+ //second is columns
+ keyM keyMInit(int rows, int cols) {
+ keyM rv(cols);
+ int i = 0;
+ for (i = 0 ; i < cols ; i++) {
+ rv[i] = keyV(rows);
+ }
+ return rv;
+ }
+
+
+
+
+ //Various key generation functions
+
+ //generates a random scalar which can be used as a secret key or mask
+ void skGen(key &sk) {
+ sk = crypto::rand<key>();
+ sc_reduce32(sk.bytes);
+ }
+
+ //generates a random scalar which can be used as a secret key or mask
+ key skGen() {
+ key sk = crypto::rand<key>();
+ sc_reduce32(sk.bytes);
+ return sk;
+ }
+
+ //Generates a vector of secret key
+ //Mainly used in testing
+ keyV skvGen(int rows ) {
+ keyV rv(rows);
+ int i = 0;
+ for (i = 0 ; i < rows ; i++) {
+ skGen(rv[i]);
+ }
+ return rv;
+ }
+
+ //generates a random curve point (for testing)
+ key pkGen() {
+ key sk = skGen();
+ key pk = scalarmultBase(sk);
+ return pk;
+ }
+
+ //generates a random secret and corresponding public key
+ void skpkGen(key &sk, key &pk) {
+ skGen(sk);
+ scalarmultBase(pk, sk);
+ }
+
+ //generates a random secret and corresponding public key
+ tuple<key, key> skpkGen() {
+ key sk = skGen();
+ key pk = scalarmultBase(sk);
+ return make_tuple(sk, pk);
+ }
+
+ //generates C =aG + bH from b, a is given..
+ void genC(key & C, const key & a, xmr_amount amount) {
+ key bH = scalarmultH(d2h(amount));
+ addKeys1(C, a, bH);
+ }
+
+ //generates a <secret , public> / Pedersen commitment to the amount
+ tuple<ctkey, ctkey> ctskpkGen(xmr_amount amount) {
+ ctkey sk, pk;
+ skpkGen(sk.dest, pk.dest);
+ skpkGen(sk.mask, pk.mask);
+ key am = d2h(amount);
+ key bH = scalarmultH(am);
+ addKeys(pk.mask, pk.mask, bH);
+ return make_tuple(sk, pk);
+ }
+
+
+ //generates a <secret , public> / Pedersen commitment but takes bH as input
+ tuple<ctkey, ctkey> ctskpkGen(key bH) {
+ ctkey sk, pk;
+ skpkGen(sk.dest, pk.dest);
+ skpkGen(sk.mask, pk.mask);
+ addKeys(pk.mask, pk.mask, bH);
+ return make_tuple(sk, pk);
+ }
+
+ key zeroCommit(xmr_amount amount) {
+ key mask = identity();
+ mask = scalarmultBase(mask);
+ key am = d2h(amount);
+ key bH = scalarmultH(am);
+ addKeys(mask, mask, bH);
+ return mask;
+ }
+
+ key commit(xmr_amount amount, key mask) {
+ mask = scalarmultBase(mask);
+ key am = d2h(amount);
+ key bH = scalarmultH(am);
+ addKeys(mask, mask, bH);
+ return mask;
+ }
+
+ //generates a random uint long long (for testing)
+ xmr_amount randXmrAmount(xmr_amount upperlimit) {
+ return h2d(skGen()) % (upperlimit);
+ }
+
+ //Scalar multiplications of curve points
+
+ //does a * G where a is a scalar and G is the curve basepoint
+ void scalarmultBase(key &aG,const key &a) {
+ ge_p3 point;
+ sc_reduce32copy(aG.bytes, a.bytes); //do this beforehand!
+ ge_scalarmult_base(&point, aG.bytes);
+ ge_p3_tobytes(aG.bytes, &point);
+ }
+
+ //does a * G where a is a scalar and G is the curve basepoint
+ key scalarmultBase(const key & a) {
+ ge_p3 point;
+ key aG;
+ sc_reduce32copy(aG.bytes, a.bytes); //do this beforehand
+ ge_scalarmult_base(&point, aG.bytes);
+ ge_p3_tobytes(aG.bytes, &point);
+ return aG;
+ }
+
+ //does a * P where a is a scalar and P is an arbitrary point
+ void scalarmultKey(key & aP, const key &P, const key &a) {
+ ge_p3 A;
+ ge_p2 R;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_scalarmult(&R, a.bytes, &A);
+ ge_tobytes(aP.bytes, &R);
+ }
+
+ //does a * P where a is a scalar and P is an arbitrary point
+ key scalarmultKey(const key & P, const key & a) {
+ ge_p3 A;
+ ge_p2 R;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_scalarmult(&R, a.bytes, &A);
+ key aP;
+ ge_tobytes(aP.bytes, &R);
+ return aP;
+ }
+
+
+ //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint
+ key scalarmultH(const key & a) {
+ ge_p3 A;
+ ge_p2 R;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_scalarmult(&R, a.bytes, &A);
+ key aP;
+ ge_tobytes(aP.bytes, &R);
+ return aP;
+ }
+
+ //Curve addition / subtractions
+
+ //for curve points: AB = A + B
+ void addKeys(key &AB, const key &A, const key &B) {
+ ge_p3 B2, A2;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_cached tmp2;
+ ge_p3_to_cached(&tmp2, &B2);
+ ge_p1p1 tmp3;
+ ge_add(&tmp3, &A2, &tmp2);
+ ge_p1p1_to_p3(&A2, &tmp3);
+ ge_p3_tobytes(AB.bytes, &A2);
+ }
+
+
+ //addKeys1
+ //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point
+ void addKeys1(key &aGB, const key &a, const key & B) {
+ key aG = scalarmultBase(a);
+ addKeys(aGB, aG, B);
+ }
+
+ //addKeys2
+ //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point
+ void addKeys2(key &aGbB, const key &a, const key &b, const key & B) {
+ ge_p2 rv;
+ ge_p3 B2;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_double_scalarmult_base_vartime(&rv, b.bytes, &B2, a.bytes);
+ ge_tobytes(aGbB.bytes, &rv);
+ }
+
+ //Does some precomputation to make addKeys3 more efficient
+ // input B a curve point and output a ge_dsmp which has precomputation applied
+ void precomp(ge_dsmp rv, const key & B) {
+ ge_p3 B2;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_dsm_precomp(rv, &B2);
+ }
+
+ //addKeys3
+ //aAbB = a*A + b*B where a, b are scalars, A, B are curve points
+ //B must be input after applying "precomp"
+ void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B) {
+ ge_p2 rv;
+ ge_p3 A2;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_double_scalarmult_precomp_vartime(&rv, a.bytes, &A2, b.bytes, B);
+ ge_tobytes(aAbB.bytes, &rv);
+ }
+
+
+ //subtract Keys (subtracts curve points)
+ //AB = A - B where A, B are curve points
+ void subKeys(key & AB, const key &A, const key &B) {
+ ge_p3 B2, A2;
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_cached tmp2;
+ ge_p3_to_cached(&tmp2, &B2);
+ ge_p1p1 tmp3;
+ ge_sub(&tmp3, &A2, &tmp2);
+ ge_p1p1_to_p3(&A2, &tmp3);
+ ge_p3_tobytes(AB.bytes, &A2);
+ }
+
+ //checks if A, B are equal as curve points
+ //without doing curve operations
+ bool equalKeys(const key & a, const key & b) {
+ bool rv = true;
+ for (int i = 0; i < 32; ++i) {
+ if (a.bytes[i] != b.bytes[i]) {
+ rv = false;
+ }
+ }
+ return rv;
+ }
+
+ //Hashing - cn_fast_hash
+ //be careful these are also in crypto namespace
+ //cn_fast_hash for arbitrary multiples of 32 bytes
+ void cn_fast_hash(key &hash, const void * data, const std::size_t l) {
+ keccak((uint8_t *)data, l, hash.bytes, 32);
+ }
+
+ void hash_to_scalar(key &hash, const void * data, const std::size_t l) {
+ cn_fast_hash(hash, data, l);
+ sc_reduce32(hash.bytes);
+ }
+
+ //cn_fast_hash for a 32 byte key
+ void cn_fast_hash(key & hash, const key & in) {
+ keccak((uint8_t *)in.bytes, 32, hash.bytes, 32);
+ }
+
+ void hash_to_scalar(key & hash, const key & in) {
+ cn_fast_hash(hash, in);
+ sc_reduce32(hash.bytes);
+ }
+
+ //cn_fast_hash for a 32 byte key
+ key cn_fast_hash(const key & in) {
+ key hash;
+ keccak((uint8_t *)in.bytes, 32, hash.bytes, 32);
+ return hash;
+ }
+
+ key hash_to_scalar(const key & in) {
+ key hash = cn_fast_hash(in);
+ sc_reduce32(hash.bytes);
+ return hash;
+ }
+
+ //cn_fast_hash for a 128 byte unsigned char
+ key cn_fast_hash128(const void * in) {
+ key hash;
+ keccak((uint8_t *)in, 128, hash.bytes, 32);
+ return hash;
+ }
+
+ key hash_to_scalar128(const void * in) {
+ key hash = cn_fast_hash128(in);
+ sc_reduce32(hash.bytes);
+ return hash;
+ }
+
+ //cn_fast_hash for multisig purpose
+ //This takes the outputs and commitments
+ //and hashes them into a 32 byte sized key
+ key cn_fast_hash(ctkeyV PC) {
+ key rv = identity();
+ std::size_t l = (std::size_t)PC.size();
+ size_t i = 0, j = 0;
+ vector<char> m(l * 64);
+ for (i = 0 ; i < l ; i++) {
+ memcpy(&m[i * 64], &PC[i].dest, 32);
+ memcpy(&m[i * 64 + 32], &PC[i].mask, 32);
+ }
+ cn_fast_hash(rv, &m[0], 64*l);
+ return rv;
+ }
+
+ key hash_to_scalar(ctkeyV PC) {
+ key rv = cn_fast_hash(PC);
+ sc_reduce32(rv.bytes);
+ return rv;
+ }
+
+ //cn_fast_hash for a key-vector of arbitrary length
+ //this is useful since you take a number of keys
+ //put them in the key vector and it concatenates them
+ //and then hashes them
+ key cn_fast_hash(const keyV &keys) {
+ size_t l = keys.size();
+ vector<unsigned char> m(l * 32);
+ size_t i;
+ for (i = 0 ; i < l ; i++) {
+ memcpy(&m[i * 32], keys[i].bytes, 32);
+ }
+ key rv;
+ cn_fast_hash(rv, &m[0], 32 * l);
+ //dp(rv);
+ return rv;
+ }
+
+ key hash_to_scalar(const keyV &keys) {
+ key rv = cn_fast_hash(keys);
+ sc_reduce32(rv.bytes);
+ return rv;
+ }
+
+ key hashToPointSimple(const key & hh) {
+ key pointk;
+ ge_p1p1 point2;
+ ge_p2 point;
+ ge_p3 res;
+ key h = cn_fast_hash(hh);
+ CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ ge_p3_to_p2(&point, &res);
+ ge_mul8(&point2, &point);
+ ge_p1p1_to_p3(&res, &point2);
+ ge_p3_tobytes(pointk.bytes, &res);
+ return pointk;
+ }
+
+ key hashToPoint(const key & hh) {
+ key pointk;
+ ge_p2 point;
+ ge_p1p1 point2;
+ ge_p3 res;
+ key h = cn_fast_hash(hh);
+ ge_fromfe_frombytes_vartime(&point, h.bytes);
+ ge_mul8(&point2, &point);
+ ge_p1p1_to_p3(&res, &point2);
+ ge_p3_tobytes(pointk.bytes, &res);
+ return pointk;
+ }
+
+ void hashToPoint(key & pointk, const key & hh) {
+ ge_p2 point;
+ ge_p1p1 point2;
+ ge_p3 res;
+ key h = cn_fast_hash(hh);
+ ge_fromfe_frombytes_vartime(&point, h.bytes);
+ ge_mul8(&point2, &point);
+ ge_p1p1_to_p3(&res, &point2);
+ ge_p3_tobytes(pointk.bytes, &res);
+ }
+
+ //sums a vector of curve points (for scalars use sc_add)
+ void sumKeys(key & Csum, const keyV & Cis) {
+ identity(Csum);
+ size_t i = 0;
+ for (i = 0; i < Cis.size(); i++) {
+ addKeys(Csum, Csum, Cis[i]);
+ }
+ }
+
+ //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a
+ // where C= aG + bH
+ void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec) {
+ key sharedSec1 = hash_to_scalar(sharedSec);
+ key sharedSec2 = hash_to_scalar(sharedSec1);
+ //encode
+ sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes);
+ sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes);
+ }
+ void ecdhDecode(ecdhTuple & masked, const key & sharedSec) {
+ key sharedSec1 = hash_to_scalar(sharedSec);
+ key sharedSec2 = hash_to_scalar(sharedSec1);
+ //decode
+ sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes);
+ sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes);
+ }
+}
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
new file mode 100644
index 000000000..1e71c645d
--- /dev/null
+++ b/src/ringct/rctOps.h
@@ -0,0 +1,171 @@
+//#define DBG
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#ifndef RCTOPS_H
+#define RCTOPS_H
+
+#include <cstddef>
+#include <mutex>
+#include <vector>
+#include <tuple>
+
+#include "crypto/generic-ops.h"
+
+extern "C" {
+#include "crypto/random.h"
+#include "crypto/keccak.h"
+#include "rctCryptoOps.h"
+}
+#include "crypto/crypto.h"
+
+#include "rctTypes.h"
+
+//Define this flag when debugging to get additional info on the console
+#ifdef DBG
+#define DP(x) dp(x)
+#else
+#define DP(x)
+#endif
+
+using namespace std;
+using namespace crypto;
+
+namespace rct {
+
+ //Various key initialization functions
+
+ //Creates a zero scalar
+ key zero();
+ void zero(key &z);
+ //Creates a zero elliptic curve point
+ key identity();
+ void identity(key &Id);
+ //copies a scalar or point
+ void copy(key &AA, const key &A);
+ key copy(const key & AA);
+ //initializes a key matrix;
+ //first parameter is rows,
+ //second is columns
+ keyM keyMInit(int, int);
+
+ //Various key generation functions
+
+ //generates a random scalar which can be used as a secret key or mask
+ key skGen();
+ void skGen(key &);
+
+ //generates a vector of secret keys of size "int"
+ keyV skvGen(int );
+
+ //generates a random curve point (for testing)
+ key pkGen();
+ //generates a random secret and corresponding public key
+ void skpkGen(key &sk, key &pk);
+ tuple<key, key> skpkGen();
+ //generates a <secret , public> / Pedersen commitment to the amount
+ tuple<ctkey, ctkey> ctskpkGen(xmr_amount amount);
+ //generates C =aG + bH from b, a is random
+ void genC(key & C, const key & a, xmr_amount amount);
+ //this one is mainly for testing, can take arbitrary amounts..
+ tuple<ctkey, ctkey> ctskpkGen(key bH);
+ // make a pedersen commitment with given key
+ key commit(xmr_amount amount, key mask);
+ // make a pedersen commitment with zero key
+ key zeroCommit(xmr_amount amount);
+ //generates a random uint long long
+ xmr_amount randXmrAmount(xmr_amount upperlimit);
+
+ //Scalar multiplications of curve points
+
+ //does a * G where a is a scalar and G is the curve basepoint
+ void scalarmultBase(key & aG, const key &a);
+ key scalarmultBase(const key & a);
+ //does a * P where a is a scalar and P is an arbitrary point
+ void scalarmultKey(key &aP, const key &P, const key &a);
+ key scalarmultKey(const key &P, const key &a);
+ //Computes aH where H= toPoint(cn_fast_hash(G)), G the basepoint
+ key scalarmultH(const key & a);
+
+ //Curve addition / subtractions
+
+ //for curve points: AB = A + B
+ void addKeys(key &AB, const key &A, const key &B);
+ //aGB = aG + B where a is a scalar, G is the basepoint, and B is a point
+ void addKeys1(key &aGB, const key &a, const key & B);
+ //aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point
+ void addKeys2(key &aGbB, const key &a, const key &b, const key &B);
+ //Does some precomputation to make addKeys3 more efficient
+ // input B a curve point and output a ge_dsmp which has precomputation applied
+ void precomp(ge_dsmp rv, const key &B);
+ //aAbB = a*A + b*B where a, b are scalars, A, B are curve points
+ //B must be input after applying "precomp"
+ void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B);
+ //AB = A - B where A, B are curve points
+ void subKeys(key &AB, const key &A, const key &B);
+ //checks if A, B are equal as curve points
+ bool equalKeys(const key & A, const key & B);
+
+ //Hashing - cn_fast_hash
+ //be careful these are also in crypto namespace
+ //cn_fast_hash for arbitrary l multiples of 32 bytes
+ void cn_fast_hash(key &hash, const void * data, const size_t l);
+ void hash_to_scalar(key &hash, const void * data, const size_t l);
+ //cn_fast_hash for a 32 byte key
+ void cn_fast_hash(key &hash, const key &in);
+ void hash_to_scalar(key &hash, const key &in);
+ //cn_fast_hash for a 32 byte key
+ key cn_fast_hash(const key &in);
+ key hash_to_scalar(const key &in);
+ //for mg sigs
+ key cn_fast_hash128(const void * in);
+ key hash_to_scalar128(const void * in);
+ key cn_fast_hash(ctkeyV PC);
+ key hash_to_scalar(ctkeyV PC);
+ //for mg sigs
+ key cn_fast_hash(const keyV &keys);
+ key hash_to_scalar(const keyV &keys);
+
+ //returns hashToPoint as described in https://github.com/ShenNoether/ge_fromfe_writeup
+ key hashToPointSimple(const key &in);
+ key hashToPoint(const key &in);
+ void hashToPoint(key &out, const key &in);
+
+ //sums a vector of curve points (for scalars use sc_add)
+ void sumKeys(key & Csum, const key &Cis);
+
+ //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a
+ // where C= aG + bH
+ void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec);
+ void ecdhDecode(ecdhTuple & masked, const key & sharedSec);
+}
+#endif /* RCTOPS_H */
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
new file mode 100644
index 000000000..f4dbd65c5
--- /dev/null
+++ b/src/ringct/rctSigs.cpp
@@ -0,0 +1,875 @@
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "misc_log_ex.h"
+#include "rctSigs.h"
+#include "cryptonote_core/cryptonote_format_utils.h"
+
+using namespace crypto;
+using namespace std;
+
+namespace rct {
+
+ //Schnorr Non-linkable
+ //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2
+ //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2
+ //These are called in the below ASNL sig generation
+
+ void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index) {
+ key c1, c2, L2;
+ key a = skGen();
+ if (index == 0) {
+ scalarmultBase(L1, a);
+ hash_to_scalar(c2, L1);
+ skGen(s2);
+ addKeys2(L2, s2, c2, P2);
+ hash_to_scalar(c1, L2);
+ //s1 = a - x * c1
+ sc_mulsub(s1.bytes, x.bytes, c1.bytes, a.bytes);
+ }
+ else if (index == 1) {
+ scalarmultBase(L2, a);
+ hash_to_scalar(c1, L2);
+ skGen(s1);
+ addKeys2(L1, s1, c1, P1);
+ hash_to_scalar(c2, L1);
+ sc_mulsub(s2.bytes, x.bytes, c2.bytes, a.bytes);
+ }
+ else {
+ throw std::runtime_error("GenSchnorrNonLinkable: invalid index (should be 0 or 1)");
+ }
+ }
+
+ //Schnorr Non-linkable
+ //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2
+ //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2
+ //These are called in the below ASNL sig generation
+ bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2) {
+ key c2, L2, c1, L1p;
+ hash_to_scalar(c2, L1);
+ addKeys2(L2, s2, c2, P2);
+ hash_to_scalar(c1, L2);
+ addKeys2(L1p, s1, c1, P1);
+
+ return equalKeys(L1, L1p);
+ }
+
+ //Aggregate Schnorr Non-linkable Ring Signature (ASNL)
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.
+ // These are used in range proofs (alternatively Borromean could be used)
+ // Gen gives a signature which proves the signer knows, for each i,
+ // an x[i] such that x[i]G = one of P1[i] or P2[i]
+ // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i
+ asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices) {
+ DP("Generating Aggregate Schnorr Non-linkable Ring Signature\n");
+ key64 s1;
+ int j = 0;
+ asnlSig rv;
+ rv.s = zero();
+ for (j = 0; j < ATOMS; j++) {
+ GenSchnorrNonLinkable(rv.L1[j], s1[j], rv.s2[j], x[j], P1[j], P2[j], (int)indices[j]);
+ sc_add(rv.s.bytes, rv.s.bytes, s1[j].bytes);
+ }
+ return rv;
+ }
+
+ //Aggregate Schnorr Non-linkable Ring Signature (ASNL)
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.
+ // These are used in range proofs (alternatively Borromean could be used)
+ // Gen gives a signature which proves the signer knows, for each i,
+ // an x[i] such that x[i]G = one of P1[i] or P2[i]
+ // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i
+ bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as) {
+ DP("Verifying Aggregate Schnorr Non-linkable Ring Signature\n");
+ key LHS = identity();
+ key RHS = scalarmultBase(as.s);
+ key c2, L2, c1;
+ int j = 0;
+ for (j = 0; j < ATOMS; j++) {
+ hash_to_scalar(c2, as.L1[j]);
+ addKeys2(L2, as.s2[j], c2, P2[j]);
+ addKeys(LHS, LHS, as.L1[j]);
+ hash_to_scalar(c1, L2);
+ addKeys(RHS, RHS, scalarmultKey(P1[j], c1));
+ }
+ key cc;
+ sc_sub(cc.bytes, LHS.bytes, RHS.bytes);
+ return sc_isnonzero(cc.bytes) == 0;
+ }
+
+ //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
+ //These are aka MG signatutes in earlier drafts of the ring ct paper
+ // c.f. http://eprint.iacr.org/2015/1098 section 2.
+ // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i
+ // Gen creates a signature which proves that for some column in the keymatrix "pk"
+ // the signer knows a secret key for each row in that column
+ // Ver verifies that the MG sig was created correctly
+ keyV keyImageV(const keyV &xx) {
+ keyV II(xx.size());
+ size_t i = 0;
+ for (i = 0; i < xx.size(); i++) {
+ II[i] = scalarmultKey(hashToPoint(scalarmultBase(xx[i])), xx[i]);
+ }
+ return II;
+ }
+
+
+ //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
+ //This is a just slghtly more efficient version than the ones described below
+ //(will be explained in more detail in Ring Multisig paper
+ //These are aka MG signatutes in earlier drafts of the ring ct paper
+ // c.f. http://eprint.iacr.org/2015/1098 section 2.
+ // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i
+ // Gen creates a signature which proves that for some column in the keymatrix "pk"
+ // the signer knows a secret key for each row in that column
+ // Ver verifies that the MG sig was created correctly
+ mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) {
+ mgSig rv;
+ size_t cols = pk.size();
+ CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!");
+ CHECK_AND_ASSERT_THROW_MES(index < cols, "Index out of range");
+ size_t rows = pk[0].size();
+ CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pk");
+ for (size_t i = 1; i < cols; ++i) {
+ CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular");
+ }
+ CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size");
+ CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size");
+
+ size_t i = 0, j = 0, ii = 0;
+ key c, c_old, L, R, Hi;
+ sc_0(c_old.bytes);
+ vector<geDsmp> Ip(dsRows);
+ rv.II = keyV(dsRows);
+ keyV alpha(rows);
+ keyV aG(rows);
+ rv.ss = keyM(cols, aG);
+ keyV aHP(dsRows);
+ keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
+ toHash[0] = message;
+ DP("here1");
+ for (i = 0; i < dsRows; i++) {
+ skpkGen(alpha[i], aG[i]); //need to save alphas for later..
+ Hi = hashToPoint(pk[index][i]);
+ aHP[i] = scalarmultKey(Hi, alpha[i]);
+ toHash[3 * i + 1] = pk[index][i];
+ toHash[3 * i + 2] = aG[i];
+ toHash[3 * i + 3] = aHP[i];
+ rv.II[i] = scalarmultKey(Hi, xx[i]);
+ precomp(Ip[i].k, rv.II[i]);
+ }
+ size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper)
+ for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) {
+ skpkGen(alpha[i], aG[i]); //need to save alphas for later..
+ toHash[ndsRows + 2 * ii + 1] = pk[index][i];
+ toHash[ndsRows + 2 * ii + 2] = aG[i];
+ }
+
+ c_old = hash_to_scalar(toHash);
+
+
+ i = (index + 1) % cols;
+ if (i == 0) {
+ copy(rv.cc, c_old);
+ }
+ while (i != index) {
+
+ rv.ss[i] = skvGen(rows);
+ sc_0(c.bytes);
+ for (j = 0; j < dsRows; j++) {
+ addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
+ hashToPoint(Hi, pk[i][j]);
+ addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
+ toHash[3 * j + 1] = pk[i][j];
+ toHash[3 * j + 2] = L;
+ toHash[3 * j + 3] = R;
+ }
+ for (j = dsRows, ii = 0; j < rows; j++, ii++) {
+ addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
+ toHash[ndsRows + 2 * ii + 1] = pk[i][j];
+ toHash[ndsRows + 2 * ii + 2] = L;
+ }
+ c = hash_to_scalar(toHash);
+ copy(c_old, c);
+ i = (i + 1) % cols;
+
+ if (i == 0) {
+ copy(rv.cc, c_old);
+ }
+ }
+ for (j = 0; j < rows; j++) {
+ sc_mulsub(rv.ss[index][j].bytes, c.bytes, xx[j].bytes, alpha[j].bytes);
+ }
+ return rv;
+ }
+
+ //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
+ //This is a just slghtly more efficient version than the ones described below
+ //(will be explained in more detail in Ring Multisig paper
+ //These are aka MG signatutes in earlier drafts of the ring ct paper
+ // c.f. http://eprint.iacr.org/2015/1098 section 2.
+ // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i
+ // Gen creates a signature which proves that for some column in the keymatrix "pk"
+ // the signer knows a secret key for each row in that column
+ // Ver verifies that the MG sig was created correctly
+ bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) {
+
+ size_t cols = pk.size();
+ CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!");
+ size_t rows = pk[0].size();
+ CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pk");
+ for (size_t i = 1; i < cols; ++i) {
+ CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular");
+ }
+ CHECK_AND_ASSERT_MES(rv.II.size() == dsRows, false, "Bad II size");
+ CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size");
+ for (size_t i = 0; i < cols; ++i) {
+ CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular");
+ }
+ CHECK_AND_ASSERT_MES(dsRows <= rows, false, "Bad dsRows value");
+
+ size_t i = 0, j = 0, ii = 0;
+ key c, L, R, Hi;
+ key c_old = copy(rv.cc);
+ vector<geDsmp> Ip(dsRows);
+ for (i = 0 ; i < dsRows ; i++) {
+ precomp(Ip[i].k, rv.II[i]);
+ }
+ size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper
+ keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
+ toHash[0] = message;
+ i = 0;
+ while (i < cols) {
+ sc_0(c.bytes);
+ for (j = 0; j < dsRows; j++) {
+ addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
+ hashToPoint(Hi, pk[i][j]);
+ addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
+ toHash[3 * j + 1] = pk[i][j];
+ toHash[3 * j + 2] = L;
+ toHash[3 * j + 3] = R;
+ }
+ for (j = dsRows, ii = 0 ; j < rows ; j++, ii++) {
+ addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
+ toHash[ndsRows + 2 * ii + 1] = pk[i][j];
+ toHash[ndsRows + 2 * ii + 2] = L;
+ }
+ c = hash_to_scalar(toHash);
+ copy(c_old, c);
+ i = (i + 1);
+ }
+ sc_sub(c.bytes, c_old.bytes, rv.cc.bytes);
+ return sc_isnonzero(c.bytes) == 0;
+ }
+
+
+
+ //proveRange and verRange
+ //proveRange gives C, and mask such that \sumCi = C
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.1
+ // and Ci is a commitment to either 0 or 2^i, i=0,...,63
+ // thus this proves that "amount" is in [0, 2^64]
+ // mask is a such that C = aG + bH, and b = amount
+ //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
+ rangeSig proveRange(key & C, key & mask, const xmr_amount & amount) {
+ sc_0(mask.bytes);
+ identity(C);
+ bits b;
+ d2b(b, amount);
+ rangeSig sig;
+ key64 ai;
+ key64 CiH;
+ int i = 0;
+ for (i = 0; i < ATOMS; i++) {
+ skGen(ai[i]);
+ if (b[i] == 0) {
+ scalarmultBase(sig.Ci[i], ai[i]);
+ }
+ if (b[i] == 1) {
+ addKeys1(sig.Ci[i], ai[i], H2[i]);
+ }
+ subKeys(CiH[i], sig.Ci[i], H2[i]);
+ sc_add(mask.bytes, mask.bytes, ai[i].bytes);
+ addKeys(C, C, sig.Ci[i]);
+ }
+ sig.asig = GenASNL(ai, sig.Ci, CiH, b);
+ return sig;
+ }
+
+ //proveRange and verRange
+ //proveRange gives C, and mask such that \sumCi = C
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.1
+ // and Ci is a commitment to either 0 or 2^i, i=0,...,63
+ // thus this proves that "amount" is in [0, 2^64]
+ // mask is a such that C = aG + bH, and b = amount
+ //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
+ bool verRange(const key & C, const rangeSig & as) {
+ key64 CiH;
+ int i = 0;
+ key Ctmp = identity();
+ for (i = 0; i < 64; i++) {
+ subKeys(CiH[i], as.Ci[i], H2[i]);
+ addKeys(Ctmp, Ctmp, as.Ci[i]);
+ }
+ if (!equalKeys(C, Ctmp))
+ return false;
+ if (!VerASNL(as.Ci, CiH, as.asig))
+ return false;
+ return true;
+ }
+
+ key get_pre_mlsag_hash(const rctSig &rv)
+ {
+ keyV hashes;
+ hashes.push_back(rv.message);
+ crypto::hash h;
+ cryptonote::get_blob_hash(cryptonote::t_serializable_object_to_blob((const rctSigBase&)rv), h);
+ hashes.push_back(hash2rct(h));
+ keyV kv;
+ for (auto r: rv.p.rangeSigs)
+ {
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.L1[n]);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.s2[n]);
+ kv.push_back(r.asig.s);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.Ci[n]);
+ }
+ hashes.push_back(cn_fast_hash(kv));
+
+ return cn_fast_hash(hashes);
+ }
+
+ //Ring-ct MG sigs
+ //Prove:
+ // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
+ // This does the MG sig on the "dest" part of the given key matrix, and
+ // the last row is the sum of input commitments from that column - sum output commitments
+ // this shows that sum inputs = sum outputs
+ //Ver:
+ // verifies the above sig is created corretly
+ mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) {
+ mgSig mg;
+ //setup vars
+ size_t cols = pubs.size();
+ CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
+ size_t rows = pubs[0].size();
+ CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pubs");
+ for (size_t i = 1; i < cols; ++i) {
+ CHECK_AND_ASSERT_THROW_MES(pubs[i].size() == rows, "pubs is not rectangular");
+ }
+ CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size");
+ CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size");
+
+ keyV sk(rows + 1);
+ keyV tmp(rows + 1);
+ size_t i = 0, j = 0;
+ for (i = 0; i < rows + 1; i++) {
+ sc_0(sk[i].bytes);
+ identity(tmp[i]);
+ }
+ keyM M(cols, tmp);
+ //create the matrix to mg sig
+ for (i = 0; i < cols; i++) {
+ M[i][rows] = identity();
+ for (j = 0; j < rows; j++) {
+ M[i][j] = pubs[i][j].dest;
+ addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add input commitments in last row
+ }
+ }
+ sc_0(sk[rows].bytes);
+ for (j = 0; j < rows; j++) {
+ sk[j] = copy(inSk[j].dest);
+ sc_add(sk[rows].bytes, sk[rows].bytes, inSk[j].mask.bytes); //add masks in last row
+ }
+ for (i = 0; i < cols; i++) {
+ for (size_t j = 0; j < outPk.size(); j++) {
+ subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row
+ }
+ //subtract txn fee output in last row
+ subKeys(M[i][rows], M[i][rows], txnFeeKey);
+ }
+ for (size_t j = 0; j < outPk.size(); j++) {
+ sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
+ }
+ return MLSAG_Gen(message, M, sk, index, rows);
+ }
+
+
+ //Ring-ct MG sigs Simple
+ // Simple version for when we assume only
+ // post rct inputs
+ // here pubs is a vector of (P, C) length mixin
+ // inSk is x, a_in corresponding to signing index
+ // a_out, Cout is for the output commitment
+ // index is the signing index..
+ mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index) {
+ mgSig mg;
+ //setup vars
+ size_t rows = 1;
+ size_t cols = pubs.size();
+ CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
+ keyV tmp(rows + 1);
+ keyV sk(rows + 1);
+ size_t i;
+ keyM M(cols, tmp);
+ for (i = 0; i < cols; i++) {
+ M[i][0] = pubs[i].dest;
+ subKeys(M[i][1], pubs[i].mask, Cout);
+ sk[0] = copy(inSk.dest);
+ sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
+ }
+ return MLSAG_Gen(message, M, sk, index, rows);
+ }
+
+
+ //Ring-ct MG sigs
+ //Prove:
+ // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
+ // This does the MG sig on the "dest" part of the given key matrix, and
+ // the last row is the sum of input commitments from that column - sum output commitments
+ // this shows that sum inputs = sum outputs
+ //Ver:
+ // verifies the above sig is created corretly
+ bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) {
+ //setup vars
+ size_t cols = pubs.size();
+ CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
+ size_t rows = pubs[0].size();
+ CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pubs");
+ for (size_t i = 1; i < cols; ++i) {
+ CHECK_AND_ASSERT_MES(pubs[i].size() == rows, false, "pubs is not rectangular");
+ }
+
+ keyV tmp(rows + 1);
+ size_t i = 0, j = 0;
+ for (i = 0; i < rows + 1; i++) {
+ identity(tmp[i]);
+ }
+ keyM M(cols, tmp);
+
+ //create the matrix to mg sig
+ for (j = 0; j < rows; j++) {
+ for (i = 0; i < cols; i++) {
+ M[i][j] = pubs[i][j].dest;
+ addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add Ci in last row
+ }
+ }
+ for (i = 0; i < cols; i++) {
+ for (j = 0; j < outPk.size(); j++) {
+ subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row
+ }
+ //subtract txn fee output in last row
+ subKeys(M[i][rows], M[i][rows], txnFeeKey);
+ }
+ return MLSAG_Ver(message, M, mg, rows);
+ }
+
+ //Ring-ct Simple MG sigs
+ //Ver:
+ //This does a simplified version, assuming only post Rct
+ //inputs
+ bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) {
+ //setup vars
+ size_t rows = 1;
+ size_t cols = pubs.size();
+ CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
+ keyV tmp(rows + 1);
+ size_t i;
+ keyM M(cols, tmp);
+ //create the matrix to mg sig
+ for (i = 0; i < cols; i++) {
+ M[i][0] = pubs[i].dest;
+ subKeys(M[i][1], pubs[i].mask, C);
+ }
+ //DP(C);
+ return MLSAG_Ver(message, M, mg, rows);
+ }
+
+ //These functions get keys from blockchain
+ //replace these when connecting blockchain
+ //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
+ //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk
+ // the return value are the key matrix, and the index where inPk was put (random).
+ void getKeyFromBlockchain(ctkey & a, size_t reference_index) {
+ a.mask = pkGen();
+ a.dest = pkGen();
+ }
+
+ //These functions get keys from blockchain
+ //replace these when connecting blockchain
+ //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
+ //populateFromBlockchain creates a keymatrix with "mixin" + 1 columns and one of the columns is inPk
+ // the return value are the key matrix, and the index where inPk was put (random).
+ tuple<ctkeyM, xmr_amount> populateFromBlockchain(ctkeyV inPk, int mixin) {
+ int rows = inPk.size();
+ ctkeyM rv(mixin + 1, inPk);
+ int index = randXmrAmount(mixin);
+ int i = 0, j = 0;
+ for (i = 0; i <= mixin; i++) {
+ if (i != index) {
+ for (j = 0; j < rows; j++) {
+ getKeyFromBlockchain(rv[i][j], (size_t)randXmrAmount);
+ }
+ }
+ }
+ return make_tuple(rv, index);
+ }
+
+ //These functions get keys from blockchain
+ //replace these when connecting blockchain
+ //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
+ //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk
+ // the return value are the key matrix, and the index where inPk was put (random).
+ xmr_amount populateFromBlockchainSimple(ctkeyV & mixRing, const ctkey & inPk, int mixin) {
+ int index = randXmrAmount(mixin);
+ int i = 0;
+ for (i = 0; i <= mixin; i++) {
+ if (i != index) {
+ getKeyFromBlockchain(mixRing[i], (size_t)randXmrAmount(1000));
+ } else {
+ mixRing[i] = inPk;
+ }
+ }
+ return index;
+ }
+
+ //RingCT protocol
+ //genRct:
+ // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
+ // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
+ // Also contains masked "amount" and "mask" so the receiver can see how much they received
+ //verRct:
+ // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
+ //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
+ // uses the attached ecdh info to find the amounts represented by each output commitment
+ // must know the destination private key to find the correct amount, else will return a random number
+ // Note: For txn fees, the last index in the amounts vector should contain that
+ // Thus the amounts vector will be "one" longer than the destinations vectort
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) {
+ CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
+ CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
+ for (size_t n = 0; n < mixRing.size(); ++n) {
+ CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size");
+ }
+
+ rctSig rv;
+ rv.type = RCTTypeFull;
+ rv.message = message;
+ rv.outPk.resize(destinations.size());
+ rv.p.rangeSigs.resize(destinations.size());
+ rv.ecdhInfo.resize(destinations.size());
+
+ size_t i = 0;
+ keyV masks(destinations.size()); //sk mask..
+ outSk.resize(destinations.size());
+ for (i = 0; i < destinations.size(); i++) {
+ //add destination to sig
+ rv.outPk[i].dest = copy(destinations[i]);
+ //compute range proof
+ rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
+ #ifdef DBG
+ CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
+ #endif
+
+ //mask amount and mask
+ rv.ecdhInfo[i].mask = copy(outSk[i].mask);
+ rv.ecdhInfo[i].amount = d2h(amounts[i]);
+ ecdhEncode(rv.ecdhInfo[i], amount_keys[i]);
+
+ }
+
+ //set txn fee
+ if (amounts.size() > destinations.size())
+ {
+ rv.txnFee = amounts[destinations.size()];
+ }
+ else
+ {
+ rv.txnFee = 0;
+ }
+ key txnFeeKey = scalarmultH(d2h(rv.txnFee));
+
+ rv.mixRing = mixRing;
+ rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey));
+ return rv;
+ }
+
+ rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin) {
+ unsigned int index;
+ ctkeyM mixRing;
+ ctkeyV outSk;
+ tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
+ return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk);
+ }
+
+ //RCT simple
+ //for post-rct only
+ rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk) {
+ CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
+ CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
+ CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
+ CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk");
+ CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk");
+ for (size_t n = 0; n < mixRing.size(); ++n) {
+ CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing");
+ }
+
+ rctSig rv;
+ rv.type = RCTTypeSimple;
+ rv.message = message;
+ rv.outPk.resize(destinations.size());
+ rv.p.rangeSigs.resize(destinations.size());
+ rv.ecdhInfo.resize(destinations.size());
+
+ size_t i;
+ keyV masks(destinations.size()); //sk mask..
+ outSk.resize(destinations.size());
+ key sumout = zero();
+ for (i = 0; i < destinations.size(); i++) {
+
+ //add destination to sig
+ rv.outPk[i].dest = copy(destinations[i]);
+ //compute range proof
+ rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
+ #ifdef DBG
+ verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ #endif
+
+ sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes);
+
+ //mask amount and mask
+ rv.ecdhInfo[i].mask = copy(outSk[i].mask);
+ rv.ecdhInfo[i].amount = d2h(outamounts[i]);
+ ecdhEncode(rv.ecdhInfo[i], amount_keys[i]);
+ }
+
+ //set txn fee
+ rv.txnFee = txnFee;
+// TODO: unused ??
+// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
+ rv.mixRing = mixRing;
+ rv.pseudoOuts.resize(inamounts.size());
+ rv.p.MGs.resize(inamounts.size());
+ key sumpouts = zero(); //sum pseudoOut masks
+ keyV a(inamounts.size());
+ for (i = 0 ; i < inamounts.size() - 1; i++) {
+ skGen(a[i]);
+ sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes);
+ genC(rv.pseudoOuts[i], a[i], inamounts[i]);
+ }
+ rv.mixRing = mixRing;
+ sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes);
+ genC(rv.pseudoOuts[i], a[i], inamounts[i]);
+ DP(rv.pseudoOuts[i]);
+
+ key full_message = get_pre_mlsag_hash(rv);
+ for (i = 0 ; i < inamounts.size(); i++) {
+ rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]);
+ }
+ return rv;
+ }
+
+ rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin) {
+ std::vector<unsigned int> index;
+ index.resize(inPk.size());
+ ctkeyM mixRing;
+ ctkeyV outSk;
+ mixRing.resize(inPk.size());
+ for (size_t i = 0; i < inPk.size(); ++i) {
+ mixRing[i].resize(mixin+1);
+ index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
+ }
+ return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk);
+ }
+
+ //RingCT protocol
+ //genRct:
+ // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
+ // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
+ // Also contains masked "amount" and "mask" so the receiver can see how much they received
+ //verRct:
+ // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
+ //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
+ // uses the attached ecdh info to find the amounts represented by each output commitment
+ // must know the destination private key to find the correct amount, else will return a random number
+ bool verRct(const rctSig & rv) {
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG");
+
+ // some rct ops can throw
+ try
+ {
+ size_t i = 0;
+ bool tmp;
+ DP("range proofs verified?");
+ for (i = 0; i < rv.outPk.size(); i++) {
+ tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ DP(tmp);
+ if (!tmp) {
+ LOG_ERROR("Range proof verification failed for input " << i);
+ return false;
+ }
+ }
+ //compute txn fee
+ key txnFeeKey = scalarmultH(d2h(rv.txnFee));
+ bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv));
+ DP("mg sig verified?");
+ DP(mgVerd);
+ if (!mgVerd) {
+ LOG_ERROR("MG signature verification failed");
+ return false;
+ }
+
+ return true;
+ }
+ catch(...)
+ {
+ return false;
+ }
+ }
+
+ //ver RingCT simple
+ //assumes only post-rct style inputs (at least for max anonymity)
+ bool verRctSimple(const rctSig & rv) {
+ size_t i = 0;
+
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig");
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs");
+ CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
+
+ key sumOutpks = identity();
+ for (i = 0; i < rv.outPk.size(); i++) {
+ if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) {
+ LOG_ERROR("Range proof verified failed for input " << i);
+ return false;
+ }
+ addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask);
+ }
+ DP(sumOutpks);
+ key txnFeeKey = scalarmultH(d2h(rv.txnFee));
+ addKeys(sumOutpks, txnFeeKey, sumOutpks);
+
+ bool tmpb = false;
+ key message = get_pre_mlsag_hash(rv);
+ key sumPseudoOuts = identity();
+ for (i = 0 ; i < rv.mixRing.size() ; i++) {
+ tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
+ addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]);
+ DP(tmpb);
+ if (!tmpb) {
+ LOG_ERROR("verRctMGSimple failed for input " << i);
+ return false;
+ }
+ }
+ DP(sumPseudoOuts);
+
+ //check pseudoOuts vs Outs..
+ if (!equalKeys(sumPseudoOuts, sumOutpks)) {
+ LOG_ERROR("Sum check failed");
+ return false;
+ }
+
+ return true;
+ }
+
+ //RingCT protocol
+ //genRct:
+ // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
+ // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
+ // Also contains masked "amount" and "mask" so the receiver can see how much they received
+ //verRct:
+ // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
+ //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
+ // uses the attached ecdh info to find the amounts represented by each output commitment
+ // must know the destination private key to find the correct amount, else will return a random number
+ xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) {
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
+ CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+
+ //mask amount and mask
+ ecdhTuple ecdh_info = rv.ecdhInfo[i];
+ ecdhDecode(ecdh_info, sk);
+ mask = ecdh_info.mask;
+ key amount = ecdh_info.amount;
+ key C = rv.outPk[i].mask;
+ DP("C");
+ DP(C);
+ key Ctmp;
+ addKeys2(Ctmp, mask, amount, H);
+ DP("Ctmp");
+ DP(Ctmp);
+ if (equalKeys(C, Ctmp) == false) {
+ CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
+ }
+ return h2d(amount);
+ }
+
+ xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i) {
+ key mask;
+ return decodeRct(rv, sk, i, mask);
+ }
+
+ xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) {
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig");
+ CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+
+ //mask amount and mask
+ ecdhTuple ecdh_info = rv.ecdhInfo[i];
+ ecdhDecode(ecdh_info, sk);
+ mask = ecdh_info.mask;
+ key amount = ecdh_info.amount;
+ key C = rv.outPk[i].mask;
+ DP("C");
+ DP(C);
+ key Ctmp;
+ addKeys2(Ctmp, mask, amount, H);
+ DP("Ctmp");
+ DP(Ctmp);
+ if (equalKeys(C, Ctmp) == false) {
+ CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
+ }
+ return h2d(amount);
+ }
+
+ xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i) {
+ key mask;
+ return decodeRctSimple(rv, sk, i, mask);
+ }
+}
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
new file mode 100644
index 000000000..f1c906d5e
--- /dev/null
+++ b/src/ringct/rctSigs.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+//#define DBG
+
+#ifndef RCTSIGS_H
+#define RCTSIGS_H
+
+#include <cstddef>
+#include <mutex>
+#include <vector>
+#include <tuple>
+
+#include "crypto/generic-ops.h"
+
+extern "C" {
+#include "crypto/random.h"
+#include "crypto/keccak.h"
+}
+#include "crypto/crypto.h"
+
+
+#include "rctTypes.h"
+#include "rctOps.h"
+
+//Define this flag when debugging to get additional info on the console
+#ifdef DBG
+#define DP(x) dp(x)
+#else
+#define DP(x)
+#endif
+
+
+
+using namespace std;
+using namespace crypto;
+
+namespace rct {
+
+ //Schnorr Non-linkable
+ //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2
+ //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2
+ //These are called in the below ASNL sig generation
+ void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index);
+ bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2);
+
+ //Aggregate Schnorr Non-linkable Ring Signature (ASNL)
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.
+ // These are used in range proofs (alternatively Borromean could be used)
+ // Gen gives a signature which proves the signer knows, for each i,
+ // an x[i] such that x[i]G = one of P1[i] or P2[i]
+ // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i
+ asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices);
+ bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as);
+
+ //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
+ //These are aka MG signatutes in earlier drafts of the ring ct paper
+ // c.f. http://eprint.iacr.org/2015/1098 section 2.
+ // keyImageV just does I[i] = xx[i] * HashToPoint(xx[i] * G) for each i
+ // Gen creates a signature which proves that for some column in the keymatrix "pk"
+ // the signer knows a secret key for each row in that column
+ // Ver verifies that the MG sig was created correctly
+ keyV keyImageV(const keyV &xx);
+ mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows);
+ bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, size_t dsRows);
+ //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
+
+ //proveRange and verRange
+ //proveRange gives C, and mask such that \sumCi = C
+ // c.f. http://eprint.iacr.org/2015/1098 section 5.1
+ // and Ci is a commitment to either 0 or 2^i, i=0,...,63
+ // thus this proves that "amount" is in [0, 2^64]
+ // mask is a such that C = aG + bH, and b = amount
+ //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
+ rangeSig proveRange(key & C, key & mask, const xmr_amount & amount);
+ bool verRange(const key & C, const rangeSig & as);
+
+ //Ring-ct MG sigs
+ //Prove:
+ // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10.
+ // This does the MG sig on the "dest" part of the given key matrix, and
+ // the last row is the sum of input commitments from that column - sum output commitments
+ // this shows that sum inputs = sum outputs
+ //Ver:
+ // verifies the above sig is created corretly
+ mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message);
+ mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index);
+ bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message);
+ bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C);
+
+ //These functions get keys from blockchain
+ //replace these when connecting blockchain
+ //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
+ //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk
+ // the return value are the key matrix, and the index where inPk was put (random).
+ void getKeyFromBlockchain(ctkey & a, size_t reference_index);
+ tuple<ctkeyM, xmr_amount> populateFromBlockchain(ctkeyV inPk, int mixin);
+
+ //RingCT protocol
+ //genRct:
+ // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
+ // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
+ // Also contains masked "amount" and "mask" so the receiver can see how much they received
+ //verRct:
+ // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
+ //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
+ // uses the attached ecdh info to find the amounts represented by each output commitment
+ // must know the destination private key to find the correct amount, else will return a random number
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk);
+ rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin);
+ rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin);
+ rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk);
+ bool verRct(const rctSig & rv);
+ bool verRctSimple(const rctSig & rv);
+ xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask);
+ xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i);
+ xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask);
+ xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i);
+}
+#endif /* RCTSIGS_H */
+
diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp
new file mode 100644
index 000000000..e773c6043
--- /dev/null
+++ b/src/ringct/rctTypes.cpp
@@ -0,0 +1,208 @@
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "rctTypes.h"
+using namespace crypto;
+using namespace std;
+
+namespace rct {
+
+ //dp
+ //Debug printing for the above types
+ //Actually use DP(value) and #define DBG
+
+ void dp(key a) {
+ int j = 0;
+ printf("\"");
+ for (j = 0; j < 32; j++) {
+ printf("%02x", (unsigned char)a.bytes[j]);
+ }
+ printf("\"");
+ printf("\n");
+ }
+
+ void dp(bool a) {
+ printf(" ... %s ... ", a ? "true" : "false");
+ printf("\n");
+ }
+
+ void dp(const char * a, int l) {
+ int j = 0;
+ printf("\"");
+ for (j = 0; j < l; j++) {
+ printf("%02x", (unsigned char)a[j]);
+ }
+ printf("\"");
+ printf("\n");
+ }
+ void dp(keyV a) {
+ size_t j = 0;
+ printf("[");
+ for (j = 0; j < a.size(); j++) {
+ dp(a[j]);
+ if (j < a.size() - 1) {
+ printf(",");
+ }
+ }
+ printf("]");
+ printf("\n");
+ }
+ void dp(keyM a) {
+ size_t j = 0;
+ printf("[");
+ for (j = 0; j < a.size(); j++) {
+ dp(a[j]);
+ if (j < a.size() - 1) {
+ printf(",");
+ }
+ }
+ printf("]");
+ printf("\n");
+ }
+ void dp(xmr_amount vali) {
+ printf("x: ");
+ std::cout << vali;
+ printf("\n\n");
+ }
+
+ void dp(int vali) {
+ printf("x: %d\n", vali);
+ printf("\n");
+ }
+ void dp(bits amountb) {
+ for (int i = 0; i < 64; i++) {
+ printf("%d", amountb[i]);
+ }
+ printf("\n");
+
+ }
+
+ void dp(const char * st) {
+ printf("%s\n", st);
+ }
+
+ //Various Conversions
+
+ //uint long long to 32 byte key
+ void d2h(key & amounth, const xmr_amount in) {
+ sc_0(amounth.bytes);
+ xmr_amount val = in;
+ int i = 0;
+ while (val != 0) {
+ amounth[i] = (unsigned char)(val & 0xFF);
+ i++;
+ val /= (xmr_amount)256;
+ }
+ }
+
+ //uint long long to 32 byte key
+ key d2h(const xmr_amount in) {
+ key amounth;
+ sc_0(amounth.bytes);
+ xmr_amount val = in;
+ int i = 0;
+ while (val != 0) {
+ amounth[i] = (unsigned char)(val & 0xFF);
+ i++;
+ val /= (xmr_amount)256;
+ }
+ return amounth;
+ }
+
+ //uint long long to int[64]
+ void d2b(bits amountb, xmr_amount val) {
+ int i = 0;
+ while (val != 0) {
+ amountb[i] = val & 1;
+ i++;
+ val >>= 1;
+ }
+ while (i < 64) {
+ amountb[i] = 0;
+ i++;
+ }
+ }
+
+ //32 byte key to uint long long
+ // if the key holds a value > 2^64
+ // then the value in the first 8 bytes is returned
+ xmr_amount h2d(const key & test) {
+ xmr_amount vali = 0;
+ int j = 0;
+ for (j = 7; j >= 0; j--) {
+ vali = (xmr_amount)(vali * 256 + (unsigned char)test.bytes[j]);
+ }
+ return vali;
+ }
+
+ //32 byte key to int[64]
+ void h2b(bits amountb2, const key & test) {
+ int val = 0, i = 0, j = 0;
+ for (j = 0; j < 8; j++) {
+ val = (unsigned char)test.bytes[j];
+ i = 8 * j;
+ while (val != 0) {
+ amountb2[i] = val & 1;
+ i++;
+ val >>= 1;
+ }
+ while (i < 8 * (j + 1)) {
+ amountb2[i] = 0;
+ }
+ }
+ }
+
+ //int[64] to 32 byte key
+ void b2h(key & amountdh, const bits amountb2) {
+ int byte, i, j;
+ for (j = 0; j < 8; j++) {
+ byte = 0;
+ i = 8 * j;
+ for (i = 7; i > -1; i--) {
+ byte = byte * 2 + amountb2[8 * j + i];
+ }
+ amountdh[j] = (unsigned char)byte;
+ }
+ for (j = 8; j < 32; j++) {
+ amountdh[j] = (unsigned char)(0x00);
+ }
+ }
+
+ //int[64] to uint long long
+ xmr_amount b2d(bits amountb) {
+ xmr_amount vali = 0;
+ int j = 0;
+ for (j = 63; j >= 0; j--) {
+ vali = (xmr_amount)(vali * 2 + amountb[j]);
+ }
+ return vali;
+ }
+
+}
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
new file mode 100644
index 000000000..f231d30fb
--- /dev/null
+++ b/src/ringct/rctTypes.h
@@ -0,0 +1,405 @@
+// Copyright (c) 2016, Monero Research Labs
+//
+// Author: Shen Noether <shen.noether@gmx.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+#ifndef RCT_TYPES_H
+#define RCT_TYPES_H
+
+#include <cstddef>
+#include <mutex>
+#include <vector>
+#include <tuple>
+#include <iostream>
+#include <cinttypes>
+
+extern "C" {
+#include "crypto/generic-ops.h"
+#include "crypto/crypto-ops.h"
+#include "crypto/random.h"
+#include "crypto/keccak.h"
+}
+#include "crypto/crypto.h"
+
+#include "serialization/serialization.h"
+#include "serialization/debug_archive.h"
+#include "serialization/binary_archive.h"
+#include "serialization/json_archive.h"
+
+
+//Define this flag when debugging to get additional info on the console
+#ifdef DBG
+#define DP(x) dp(x)
+#else
+#define DP(x)
+#endif
+
+//atomic units of moneros
+#define ATOMS 64
+
+//for printing large ints
+
+using namespace std;
+using namespace crypto;
+
+//Namespace specifically for ring ct code
+namespace rct {
+ //basic ops containers
+ typedef unsigned char * Bytes;
+
+ // Can contain a secret or public key
+ // similar to secret_key / public_key of crypto-ops,
+ // but uses unsigned chars,
+ // also includes an operator for accessing the i'th byte.
+ struct key {
+ unsigned char & operator[](int i) {
+ return bytes[i];
+ }
+ unsigned char operator[](int i) const {
+ return bytes[i];
+ }
+ bool operator==(const key &k) const { return !memcmp(bytes, k.bytes, sizeof(bytes)); }
+ unsigned char bytes[32];
+ };
+ typedef vector<key> keyV; //vector of keys
+ typedef vector<keyV> keyM; //matrix of keys (indexed by column first)
+
+ //containers For CT operations
+ //if it's representing a private ctkey then "dest" contains the secret key of the address
+ // while "mask" contains a where C = aG + bH is CT pedersen commitment and b is the amount
+ // (store b, the amount, separately
+ //if it's representing a public ctkey, then "dest" = P the address, mask = C the commitment
+ struct ctkey {
+ key dest;
+ key mask; //C here if public
+ };
+ typedef vector<ctkey> ctkeyV;
+ typedef vector<ctkeyV> ctkeyM;
+
+ //data for passing the amount to the receiver secretly
+ // If the pedersen commitment to an amount is C = aG + bH,
+ // "mask" contains a 32 byte key a
+ // "amount" contains a hex representation (in 32 bytes) of a 64 bit number
+ // "senderPk" is not the senders actual public key, but a one-time public key generated for
+ // the purpose of the ECDH exchange
+ struct ecdhTuple {
+ key mask;
+ key amount;
+ key senderPk;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(mask)
+ FIELD(amount)
+ // FIELD(senderPk) // not serialized, as we do not use it in monero currently
+ END_SERIALIZE()
+ };
+
+ //containers for representing amounts
+ typedef uint64_t xmr_amount;
+ typedef unsigned int bits[ATOMS];
+ typedef key key64[64];
+
+ //just contains the necessary keys to represent asnlSigs
+ //c.f. http://eprint.iacr.org/2015/1098
+ struct asnlSig {
+ key64 L1;
+ key64 s2;
+ key s;
+ };
+
+ //Container for precomp
+ struct geDsmp {
+ ge_dsmp k;
+ };
+
+ //just contains the necessary keys to represent MLSAG sigs
+ //c.f. http://eprint.iacr.org/2015/1098
+ struct mgSig {
+ keyM ss;
+ key cc;
+ keyV II;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(ss)
+ FIELD(cc)
+ // FIELD(II) - not serialized, it can be reconstructed
+ END_SERIALIZE()
+ };
+ //contains the data for an asnl sig
+ // also contains the "Ci" values such that
+ // \sum Ci = C
+ // and the signature proves that each Ci is either
+ // a Pedersen commitment to 0 or to 2^i
+ //thus proving that C is in the range of [0, 2^64]
+ struct rangeSig {
+ asnlSig asig;
+ key64 Ci;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(asig)
+ FIELD(Ci)
+ END_SERIALIZE()
+ };
+ //A container to hold all signatures necessary for RingCT
+ // rangeSigs holds all the rangeproof data of a transaction
+ // MG holds the MLSAG signature of a transaction
+ // mixRing holds all the public keypairs (P, C) for a transaction
+ // ecdhInfo holds an encoded mask / amount to be passed to each receiver
+ // outPk contains public keypairs which are destinations (P, C),
+ // P = address, C = commitment to amount
+ enum {
+ RCTTypeNull = 0,
+ RCTTypeFull = 1,
+ RCTTypeSimple = 2,
+ };
+ struct rctSigBase {
+ uint8_t type;
+ key message;
+ ctkeyM mixRing; //the set of all pubkeys / copy
+ //pairs that you mix with
+ keyV pseudoOuts; //C - for simple rct
+ vector<ecdhTuple> ecdhInfo;
+ ctkeyV outPk;
+ xmr_amount txnFee; // contains b
+
+ BEGIN_SERIALIZE()
+ FIELD(type)
+ if (type == RCTTypeNull)
+ return true;
+ // FIELD(message) - not serialized, it can be reconstructed
+ // FIELD(mixRing) - not serialized, it can be reconstructed
+ if (type == RCTTypeSimple)
+ FIELD(pseudoOuts)
+ FIELD(ecdhInfo)
+ if (typename Archive<W>::is_saving()) {
+ keyV outPk(this->outPk.size());
+ for (size_t n = 0; n < outPk.size(); ++n)
+ outPk[n] = this->outPk[n].mask;
+ FIELD(outPk)
+ }
+ else {
+ keyV outPk;
+ FIELD(outPk)
+ this->outPk.resize(outPk.size());
+ for (size_t n = 0; n < outPk.size(); ++n)
+ this->outPk[n].mask = outPk[n];
+ }
+ VARINT_FIELD(txnFee)
+ END_SERIALIZE()
+ };
+ struct rctSigPrunable {
+ vector<rangeSig> rangeSigs;
+ vector<mgSig> MGs; // simple rct has N, full has 1
+
+ BEGIN_SERIALIZE()
+ FIELD(rangeSigs)
+ FIELD(MGs)
+ END_SERIALIZE()
+ };
+ struct rctSig: public rctSigBase {
+ rctSigPrunable p;
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELDS(*static_cast<rctSigBase *>(this))
+ if (type == RCTTypeNull)
+ return true;
+ FIELDS(p);
+ END_SERIALIZE()
+ };
+
+ //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint
+ static const key H = { {0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94} };
+
+ //H2 contains 2^i H in each index, i.e. H, 2H, 4H, 8H, ...
+ //This is used for the range proofG
+ //You can regenerate this by running python2 Test.py HPow2 in the MiniNero repo
+ static const key64 H2 = {{0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94},
+ {0x8f, 0xaa, 0x44, 0x8a, 0xe4, 0xb3, 0xe2, 0xbb, 0x3d, 0x4d, 0x13, 0x09, 0x09, 0xf5, 0x5f, 0xcd, 0x79, 0x71, 0x1c, 0x1c, 0x83, 0xcd, 0xbc, 0xca, 0xdd, 0x42, 0xcb, 0xe1, 0x51, 0x5e, 0x87, 0x12},
+ {0x12, 0xa7, 0xd6, 0x2c, 0x77, 0x91, 0x65, 0x4a, 0x57, 0xf3, 0xe6, 0x76, 0x94, 0xed, 0x50, 0xb4, 0x9a, 0x7d, 0x9e, 0x3f, 0xc1, 0xe4, 0xc7, 0xa0, 0xbd, 0xe2, 0x9d, 0x18, 0x7e, 0x9c, 0xc7, 0x1d},
+ {0x78, 0x9a, 0xb9, 0x93, 0x4b, 0x49, 0xc4, 0xf9, 0xe6, 0x78, 0x5c, 0x6d, 0x57, 0xa4, 0x98, 0xb3, 0xea, 0xd4, 0x43, 0xf0, 0x4f, 0x13, 0xdf, 0x11, 0x0c, 0x54, 0x27, 0xb4, 0xf2, 0x14, 0xc7, 0x39},
+ {0x77, 0x1e, 0x92, 0x99, 0xd9, 0x4f, 0x02, 0xac, 0x72, 0xe3, 0x8e, 0x44, 0xde, 0x56, 0x8a, 0xc1, 0xdc, 0xb2, 0xed, 0xc6, 0xed, 0xb6, 0x1f, 0x83, 0xca, 0x41, 0x8e, 0x10, 0x77, 0xce, 0x3d, 0xe8},
+ {0x73, 0xb9, 0x6d, 0xb4, 0x30, 0x39, 0x81, 0x9b, 0xda, 0xf5, 0x68, 0x0e, 0x5c, 0x32, 0xd7, 0x41, 0x48, 0x88, 0x84, 0xd1, 0x8d, 0x93, 0x86, 0x6d, 0x40, 0x74, 0xa8, 0x49, 0x18, 0x2a, 0x8a, 0x64},
+ {0x8d, 0x45, 0x8e, 0x1c, 0x2f, 0x68, 0xeb, 0xeb, 0xcc, 0xd2, 0xfd, 0x5d, 0x37, 0x9f, 0x5e, 0x58, 0xf8, 0x13, 0x4d, 0xf3, 0xe0, 0xe8, 0x8c, 0xad, 0x3d, 0x46, 0x70, 0x10, 0x63, 0xa8, 0xd4, 0x12},
+ {0x09, 0x55, 0x1e, 0xdb, 0xe4, 0x94, 0x41, 0x8e, 0x81, 0x28, 0x44, 0x55, 0xd6, 0x4b, 0x35, 0xee, 0x8a, 0xc0, 0x93, 0x06, 0x8a, 0x5f, 0x16, 0x1f, 0xa6, 0x63, 0x75, 0x59, 0x17, 0x7e, 0xf4, 0x04},
+ {0xd0, 0x5a, 0x88, 0x66, 0xf4, 0xdf, 0x8c, 0xee, 0x1e, 0x26, 0x8b, 0x1d, 0x23, 0xa4, 0xc5, 0x8c, 0x92, 0xe7, 0x60, 0x30, 0x97, 0x86, 0xcd, 0xac, 0x0f, 0xed, 0xa1, 0xd2, 0x47, 0xa9, 0xc9, 0xa7},
+ {0x55, 0xcd, 0xaa, 0xd5, 0x18, 0xbd, 0x87, 0x1d, 0xd1, 0xeb, 0x7b, 0xc7, 0x02, 0x3e, 0x1d, 0xc0, 0xfd, 0xf3, 0x33, 0x98, 0x64, 0xf8, 0x8f, 0xdd, 0x2d, 0xe2, 0x69, 0xfe, 0x9e, 0xe1, 0x83, 0x2d},
+ {0xe7, 0x69, 0x7e, 0x95, 0x1a, 0x98, 0xcf, 0xd5, 0x71, 0x2b, 0x84, 0xbb, 0xe5, 0xf3, 0x4e, 0xd7, 0x33, 0xe9, 0x47, 0x3f, 0xcb, 0x68, 0xed, 0xa6, 0x6e, 0x37, 0x88, 0xdf, 0x19, 0x58, 0xc3, 0x06},
+ {0xf9, 0x2a, 0x97, 0x0b, 0xae, 0x72, 0x78, 0x29, 0x89, 0xbf, 0xc8, 0x3a, 0xdf, 0xaa, 0x92, 0xa4, 0xf4, 0x9c, 0x7e, 0x95, 0x91, 0x8b, 0x3b, 0xba, 0x3c, 0xdc, 0x7f, 0xe8, 0x8a, 0xcc, 0x8d, 0x47},
+ {0x1f, 0x66, 0xc2, 0xd4, 0x91, 0xd7, 0x5a, 0xf9, 0x15, 0xc8, 0xdb, 0x6a, 0x6d, 0x1c, 0xb0, 0xcd, 0x4f, 0x7d, 0xdc, 0xd5, 0xe6, 0x3d, 0x3b, 0xa9, 0xb8, 0x3c, 0x86, 0x6c, 0x39, 0xef, 0x3a, 0x2b},
+ {0x3e, 0xec, 0x98, 0x84, 0xb4, 0x3f, 0x58, 0xe9, 0x3e, 0xf8, 0xde, 0xea, 0x26, 0x00, 0x04, 0xef, 0xea, 0x2a, 0x46, 0x34, 0x4f, 0xc5, 0x96, 0x5b, 0x1a, 0x7d, 0xd5, 0xd1, 0x89, 0x97, 0xef, 0xa7},
+ {0xb2, 0x9f, 0x8f, 0x0c, 0xcb, 0x96, 0x97, 0x7f, 0xe7, 0x77, 0xd4, 0x89, 0xd6, 0xbe, 0x9e, 0x7e, 0xbc, 0x19, 0xc4, 0x09, 0xb5, 0x10, 0x35, 0x68, 0xf2, 0x77, 0x61, 0x1d, 0x7e, 0xa8, 0x48, 0x94},
+ {0x56, 0xb1, 0xf5, 0x12, 0x65, 0xb9, 0x55, 0x98, 0x76, 0xd5, 0x8d, 0x24, 0x9d, 0x0c, 0x14, 0x6d, 0x69, 0xa1, 0x03, 0x63, 0x66, 0x99, 0x87, 0x4d, 0x3f, 0x90, 0x47, 0x35, 0x50, 0xfe, 0x3f, 0x2c},
+ {0x1d, 0x7a, 0x36, 0x57, 0x5e, 0x22, 0xf5, 0xd1, 0x39, 0xff, 0x9c, 0xc5, 0x10, 0xfa, 0x13, 0x85, 0x05, 0x57, 0x6b, 0x63, 0x81, 0x5a, 0x94, 0xe4, 0xb0, 0x12, 0xbf, 0xd4, 0x57, 0xca, 0xaa, 0xda},
+ {0xd0, 0xac, 0x50, 0x7a, 0x86, 0x4e, 0xcd, 0x05, 0x93, 0xfa, 0x67, 0xbe, 0x7d, 0x23, 0x13, 0x43, 0x92, 0xd0, 0x0e, 0x40, 0x07, 0xe2, 0x53, 0x48, 0x78, 0xd9, 0xb2, 0x42, 0xe1, 0x0d, 0x76, 0x20},
+ {0xf6, 0xc6, 0x84, 0x0b, 0x9c, 0xf1, 0x45, 0xbb, 0x2d, 0xcc, 0xf8, 0x6e, 0x94, 0x0b, 0xe0, 0xfc, 0x09, 0x8e, 0x32, 0xe3, 0x10, 0x99, 0xd5, 0x6f, 0x7f, 0xe0, 0x87, 0xbd, 0x5d, 0xeb, 0x50, 0x94},
+ {0x28, 0x83, 0x1a, 0x33, 0x40, 0x07, 0x0e, 0xb1, 0xdb, 0x87, 0xc1, 0x2e, 0x05, 0x98, 0x0d, 0x5f, 0x33, 0xe9, 0xef, 0x90, 0xf8, 0x3a, 0x48, 0x17, 0xc9, 0xf4, 0xa0, 0xa3, 0x32, 0x27, 0xe1, 0x97},
+ {0x87, 0x63, 0x22, 0x73, 0xd6, 0x29, 0xcc, 0xb7, 0xe1, 0xed, 0x1a, 0x76, 0x8f, 0xa2, 0xeb, 0xd5, 0x17, 0x60, 0xf3, 0x2e, 0x1c, 0x0b, 0x86, 0x7a, 0x5d, 0x36, 0x8d, 0x52, 0x71, 0x05, 0x5c, 0x6e},
+ {0x5c, 0x7b, 0x29, 0x42, 0x43, 0x47, 0x96, 0x4d, 0x04, 0x27, 0x55, 0x17, 0xc5, 0xae, 0x14, 0xb6, 0xb5, 0xea, 0x27, 0x98, 0xb5, 0x73, 0xfc, 0x94, 0xe6, 0xe4, 0x4a, 0x53, 0x21, 0x60, 0x0c, 0xfb},
+ {0xe6, 0x94, 0x50, 0x42, 0xd7, 0x8b, 0xc2, 0xc3, 0xbd, 0x6e, 0xc5, 0x8c, 0x51, 0x1a, 0x9f, 0xe8, 0x59, 0xc0, 0xad, 0x63, 0xfd, 0xe4, 0x94, 0xf5, 0x03, 0x9e, 0x0e, 0x82, 0x32, 0x61, 0x2b, 0xd5},
+ {0x36, 0xd5, 0x69, 0x07, 0xe2, 0xec, 0x74, 0x5d, 0xb6, 0xe5, 0x4f, 0x0b, 0x2e, 0x1b, 0x23, 0x00, 0xab, 0xcb, 0x42, 0x2e, 0x71, 0x2d, 0xa5, 0x88, 0xa4, 0x0d, 0x3f, 0x1e, 0xbb, 0xbe, 0x02, 0xf6},
+ {0x34, 0xdb, 0x6e, 0xe4, 0xd0, 0x60, 0x8e, 0x5f, 0x78, 0x36, 0x50, 0x49, 0x5a, 0x3b, 0x2f, 0x52, 0x73, 0xc5, 0x13, 0x4e, 0x52, 0x84, 0xe4, 0xfd, 0xf9, 0x66, 0x27, 0xbb, 0x16, 0xe3, 0x1e, 0x6b},
+ {0x8e, 0x76, 0x59, 0xfb, 0x45, 0xa3, 0x78, 0x7d, 0x67, 0x4a, 0xe8, 0x67, 0x31, 0xfa, 0xa2, 0x53, 0x8e, 0xc0, 0xfd, 0xf4, 0x42, 0xab, 0x26, 0xe9, 0xc7, 0x91, 0xfa, 0xda, 0x08, 0x94, 0x67, 0xe9},
+ {0x30, 0x06, 0xcf, 0x19, 0x8b, 0x24, 0xf3, 0x1b, 0xb4, 0xc7, 0xe6, 0x34, 0x60, 0x00, 0xab, 0xc7, 0x01, 0xe8, 0x27, 0xcf, 0xbb, 0x5d, 0xf5, 0x2d, 0xcf, 0xa4, 0x2e, 0x9c, 0xa9, 0xff, 0x08, 0x02},
+ {0xf5, 0xfd, 0x40, 0x3c, 0xb6, 0xe8, 0xbe, 0x21, 0x47, 0x2e, 0x37, 0x7f, 0xfd, 0x80, 0x5a, 0x8c, 0x60, 0x83, 0xea, 0x48, 0x03, 0xb8, 0x48, 0x53, 0x89, 0xcc, 0x3e, 0xbc, 0x21, 0x5f, 0x00, 0x2a},
+ {0x37, 0x31, 0xb2, 0x60, 0xeb, 0x3f, 0x94, 0x82, 0xe4, 0x5f, 0x1c, 0x3f, 0x3b, 0x9d, 0xcf, 0x83, 0x4b, 0x75, 0xe6, 0xee, 0xf8, 0xc4, 0x0f, 0x46, 0x1e, 0xa2, 0x7e, 0x8b, 0x6e, 0xd9, 0x47, 0x3d},
+ {0x9f, 0x9d, 0xab, 0x09, 0xc3, 0xf5, 0xe4, 0x28, 0x55, 0xc2, 0xde, 0x97, 0x1b, 0x65, 0x93, 0x28, 0xa2, 0xdb, 0xc4, 0x54, 0x84, 0x5f, 0x39, 0x6f, 0xfc, 0x05, 0x3f, 0x0b, 0xb1, 0x92, 0xf8, 0xc3},
+ {0x5e, 0x05, 0x5d, 0x25, 0xf8, 0x5f, 0xdb, 0x98, 0xf2, 0x73, 0xe4, 0xaf, 0xe0, 0x84, 0x64, 0xc0, 0x03, 0xb7, 0x0f, 0x1e, 0xf0, 0x67, 0x7b, 0xb5, 0xe2, 0x57, 0x06, 0x40, 0x0b, 0xe6, 0x20, 0xa5},
+ {0x86, 0x8b, 0xcf, 0x36, 0x79, 0xcb, 0x6b, 0x50, 0x0b, 0x94, 0x41, 0x8c, 0x0b, 0x89, 0x25, 0xf9, 0x86, 0x55, 0x30, 0x30, 0x3a, 0xe4, 0xe4, 0xb2, 0x62, 0x59, 0x18, 0x65, 0x66, 0x6a, 0x45, 0x90},
+ {0xb3, 0xdb, 0x6b, 0xd3, 0x89, 0x7a, 0xfb, 0xd1, 0xdf, 0x3f, 0x96, 0x44, 0xab, 0x21, 0xc8, 0x05, 0x0e, 0x1f, 0x00, 0x38, 0xa5, 0x2f, 0x7c, 0xa9, 0x5a, 0xc0, 0xc3, 0xde, 0x75, 0x58, 0xcb, 0x7a},
+ {0x81, 0x19, 0xb3, 0xa0, 0x59, 0xff, 0x2c, 0xac, 0x48, 0x3e, 0x69, 0xbc, 0xd4, 0x1d, 0x6d, 0x27, 0x14, 0x94, 0x47, 0x91, 0x42, 0x88, 0xbb, 0xea, 0xee, 0x34, 0x13, 0xe6, 0xdc, 0xc6, 0xd1, 0xeb},
+ {0x10, 0xfc, 0x58, 0xf3, 0x5f, 0xc7, 0xfe, 0x7a, 0xe8, 0x75, 0x52, 0x4b, 0xb5, 0x85, 0x00, 0x03, 0x00, 0x5b, 0x7f, 0x97, 0x8c, 0x0c, 0x65, 0xe2, 0xa9, 0x65, 0x46, 0x4b, 0x6d, 0x00, 0x81, 0x9c},
+ {0x5a, 0xcd, 0x94, 0xeb, 0x3c, 0x57, 0x83, 0x79, 0xc1, 0xea, 0x58, 0xa3, 0x43, 0xec, 0x4f, 0xcf, 0xf9, 0x62, 0x77, 0x6f, 0xe3, 0x55, 0x21, 0xe4, 0x75, 0xa0, 0xe0, 0x6d, 0x88, 0x7b, 0x2d, 0xb9},
+ {0x33, 0xda, 0xf3, 0xa2, 0x14, 0xd6, 0xe0, 0xd4, 0x2d, 0x23, 0x00, 0xa7, 0xb4, 0x4b, 0x39, 0x29, 0x0d, 0xb8, 0x98, 0x9b, 0x42, 0x79, 0x74, 0xcd, 0x86, 0x5d, 0xb0, 0x11, 0x05, 0x5a, 0x29, 0x01},
+ {0xcf, 0xc6, 0x57, 0x2f, 0x29, 0xaf, 0xd1, 0x64, 0xa4, 0x94, 0xe6, 0x4e, 0x6f, 0x1a, 0xeb, 0x82, 0x0c, 0x3e, 0x7d, 0xa3, 0x55, 0x14, 0x4e, 0x51, 0x24, 0xa3, 0x91, 0xd0, 0x6e, 0x9f, 0x95, 0xea},
+ {0xd5, 0x31, 0x2a, 0x4b, 0x0e, 0xf6, 0x15, 0xa3, 0x31, 0xf6, 0x35, 0x2c, 0x2e, 0xd2, 0x1d, 0xac, 0x9e, 0x7c, 0x36, 0x39, 0x8b, 0x93, 0x9a, 0xec, 0x90, 0x1c, 0x25, 0x7f, 0x6c, 0xbc, 0x9e, 0x8e},
+ {0x55, 0x1d, 0x67, 0xfe, 0xfc, 0x7b, 0x5b, 0x9f, 0x9f, 0xdb, 0xf6, 0xaf, 0x57, 0xc9, 0x6c, 0x8a, 0x74, 0xd7, 0xe4, 0x5a, 0x00, 0x20, 0x78, 0xa7, 0xb5, 0xba, 0x45, 0xc6, 0xfd, 0xe9, 0x3e, 0x33},
+ {0xd5, 0x0a, 0xc7, 0xbd, 0x5c, 0xa5, 0x93, 0xc6, 0x56, 0x92, 0x8f, 0x38, 0x42, 0x80, 0x17, 0xfc, 0x7b, 0xa5, 0x02, 0x85, 0x4c, 0x43, 0xd8, 0x41, 0x49, 0x50, 0xe9, 0x6e, 0xcb, 0x40, 0x5d, 0xc3},
+ {0x07, 0x73, 0xe1, 0x8e, 0xa1, 0xbe, 0x44, 0xfe, 0x1a, 0x97, 0xe2, 0x39, 0x57, 0x3c, 0xfa, 0xe3, 0xe4, 0xe9, 0x5e, 0xf9, 0xaa, 0x9f, 0xaa, 0xbe, 0xac, 0x12, 0x74, 0xd3, 0xad, 0x26, 0x16, 0x04},
+ {0xe9, 0xaf, 0x0e, 0x7c, 0xa8, 0x93, 0x30, 0xd2, 0xb8, 0x61, 0x5d, 0x1b, 0x41, 0x37, 0xca, 0x61, 0x7e, 0x21, 0x29, 0x7f, 0x2f, 0x0d, 0xed, 0x8e, 0x31, 0xb7, 0xd2, 0xea, 0xd8, 0x71, 0x46, 0x60},
+ {0x7b, 0x12, 0x45, 0x83, 0x09, 0x7f, 0x10, 0x29, 0xa0, 0xc7, 0x41, 0x91, 0xfe, 0x73, 0x78, 0xc9, 0x10, 0x5a, 0xcc, 0x70, 0x66, 0x95, 0xed, 0x14, 0x93, 0xbb, 0x76, 0x03, 0x42, 0x26, 0xa5, 0x7b},
+ {0xec, 0x40, 0x05, 0x7b, 0x99, 0x54, 0x76, 0x65, 0x0b, 0x3d, 0xb9, 0x8e, 0x9d, 0xb7, 0x57, 0x38, 0xa8, 0xcd, 0x2f, 0x94, 0xd8, 0x63, 0xb9, 0x06, 0x15, 0x0c, 0x56, 0xaa, 0xc1, 0x9c, 0xaa, 0x6b},
+ {0x01, 0xd9, 0xff, 0x72, 0x9e, 0xfd, 0x39, 0xd8, 0x37, 0x84, 0xc0, 0xfe, 0x59, 0xc4, 0xae, 0x81, 0xa6, 0x70, 0x34, 0xcb, 0x53, 0xc9, 0x43, 0xfb, 0x81, 0x8b, 0x9d, 0x8a, 0xe7, 0xfc, 0x33, 0xe5},
+ {0x00, 0xdf, 0xb3, 0xc6, 0x96, 0x32, 0x8c, 0x76, 0x42, 0x45, 0x19, 0xa7, 0xbe, 0xfe, 0x8e, 0x0f, 0x6c, 0x76, 0xf9, 0x47, 0xb5, 0x27, 0x67, 0x91, 0x6d, 0x24, 0x82, 0x3f, 0x73, 0x5b, 0xaf, 0x2e},
+ {0x46, 0x1b, 0x79, 0x9b, 0x4d, 0x9c, 0xee, 0xa8, 0xd5, 0x80, 0xdc, 0xb7, 0x6d, 0x11, 0x15, 0x0d, 0x53, 0x5e, 0x16, 0x39, 0xd1, 0x60, 0x03, 0xc3, 0xfb, 0x7e, 0x9d, 0x1f, 0xd1, 0x30, 0x83, 0xa8},
+ {0xee, 0x03, 0x03, 0x94, 0x79, 0xe5, 0x22, 0x8f, 0xdc, 0x55, 0x1c, 0xbd, 0xe7, 0x07, 0x9d, 0x34, 0x12, 0xea, 0x18, 0x6a, 0x51, 0x7c, 0xcc, 0x63, 0xe4, 0x6e, 0x9f, 0xcc, 0xe4, 0xfe, 0x3a, 0x6c},
+ {0xa8, 0xcf, 0xb5, 0x43, 0x52, 0x4e, 0x7f, 0x02, 0xb9, 0xf0, 0x45, 0xac, 0xd5, 0x43, 0xc2, 0x1c, 0x37, 0x3b, 0x4c, 0x9b, 0x98, 0xac, 0x20, 0xce, 0xc4, 0x17, 0xa6, 0xdd, 0xb5, 0x74, 0x4e, 0x94},
+ {0x93, 0x2b, 0x79, 0x4b, 0xf8, 0x9c, 0x6e, 0xda, 0xf5, 0xd0, 0x65, 0x0c, 0x7c, 0x4b, 0xad, 0x92, 0x42, 0xb2, 0x56, 0x26, 0xe3, 0x7e, 0xad, 0x5a, 0xa7, 0x5e, 0xc8, 0xc6, 0x4e, 0x09, 0xdd, 0x4f},
+ {0x16, 0xb1, 0x0c, 0x77, 0x9c, 0xe5, 0xcf, 0xef, 0x59, 0xc7, 0x71, 0x0d, 0x2e, 0x68, 0x44, 0x1e, 0xa6, 0xfa, 0xcb, 0x68, 0xe9, 0xb5, 0xf7, 0xd5, 0x33, 0xae, 0x0b, 0xb7, 0x8e, 0x28, 0xbf, 0x57},
+ {0x0f, 0x77, 0xc7, 0x67, 0x43, 0xe7, 0x39, 0x6f, 0x99, 0x10, 0x13, 0x9f, 0x49, 0x37, 0xd8, 0x37, 0xae, 0x54, 0xe2, 0x10, 0x38, 0xac, 0x5c, 0x0b, 0x3f, 0xd6, 0xef, 0x17, 0x1a, 0x28, 0xa7, 0xe4},
+ {0xd7, 0xe5, 0x74, 0xb7, 0xb9, 0x52, 0xf2, 0x93, 0xe8, 0x0d, 0xde, 0x90, 0x5e, 0xb5, 0x09, 0x37, 0x3f, 0x3f, 0x6c, 0xd1, 0x09, 0xa0, 0x22, 0x08, 0xb3, 0xc1, 0xe9, 0x24, 0x08, 0x0a, 0x20, 0xca},
+ {0x45, 0x66, 0x6f, 0x8c, 0x38, 0x1e, 0x3d, 0xa6, 0x75, 0x56, 0x3f, 0xf8, 0xba, 0x23, 0xf8, 0x3b, 0xfa, 0xc3, 0x0c, 0x34, 0xab, 0xdd, 0xe6, 0xe5, 0xc0, 0x97, 0x5e, 0xf9, 0xfd, 0x70, 0x0c, 0xb9},
+ {0xb2, 0x46, 0x12, 0xe4, 0x54, 0x60, 0x7e, 0xb1, 0xab, 0xa4, 0x47, 0xf8, 0x16, 0xd1, 0xa4, 0x55, 0x1e, 0xf9, 0x5f, 0xa7, 0x24, 0x7f, 0xb7, 0xc1, 0xf5, 0x03, 0x02, 0x0a, 0x71, 0x77, 0xf0, 0xdd},
+ {0x7e, 0x20, 0x88, 0x61, 0x85, 0x6d, 0xa4, 0x2c, 0x8b, 0xb4, 0x6a, 0x75, 0x67, 0xf8, 0x12, 0x13, 0x62, 0xd9, 0xfb, 0x24, 0x96, 0xf1, 0x31, 0xa4, 0xaa, 0x90, 0x17, 0xcf, 0x36, 0x6c, 0xdf, 0xce},
+ {0x5b, 0x64, 0x6b, 0xff, 0x6a, 0xd1, 0x10, 0x01, 0x65, 0x03, 0x7a, 0x05, 0x56, 0x01, 0xea, 0x02, 0x35, 0x8c, 0x0f, 0x41, 0x05, 0x0f, 0x9d, 0xfe, 0x3c, 0x95, 0xdc, 0xcb, 0xd3, 0x08, 0x7b, 0xe0},
+ {0x74, 0x6d, 0x1d, 0xcc, 0xfe, 0xd2, 0xf0, 0xff, 0x1e, 0x13, 0xc5, 0x1e, 0x2d, 0x50, 0xd5, 0x32, 0x43, 0x75, 0xfb, 0xd5, 0xbf, 0x7c, 0xa8, 0x2a, 0x89, 0x31, 0x82, 0x8d, 0x80, 0x1d, 0x43, 0xab},
+ {0xcb, 0x98, 0x11, 0x0d, 0x4a, 0x6b, 0xb9, 0x7d, 0x22, 0xfe, 0xad, 0xbc, 0x6c, 0x0d, 0x89, 0x30, 0xc5, 0xf8, 0xfc, 0x50, 0x8b, 0x2f, 0xc5, 0xb3, 0x53, 0x28, 0xd2, 0x6b, 0x88, 0xdb, 0x19, 0xae},
+ {0x60, 0xb6, 0x26, 0xa0, 0x33, 0xb5, 0x5f, 0x27, 0xd7, 0x67, 0x6c, 0x40, 0x95, 0xea, 0xba, 0xbc, 0x7a, 0x2c, 0x7e, 0xde, 0x26, 0x24, 0xb4, 0x72, 0xe9, 0x7f, 0x64, 0xf9, 0x6b, 0x8c, 0xfc, 0x0e},
+ {0xe5, 0xb5, 0x2b, 0xc9, 0x27, 0x46, 0x8d, 0xf7, 0x18, 0x93, 0xeb, 0x81, 0x97, 0xef, 0x82, 0x0c, 0xf7, 0x6c, 0xb0, 0xaa, 0xf6, 0xe8, 0xe4, 0xfe, 0x93, 0xad, 0x62, 0xd8, 0x03, 0x98, 0x31, 0x04},
+ {0x05, 0x65, 0x41, 0xae, 0x5d, 0xa9, 0x96, 0x1b, 0xe2, 0xb0, 0xa5, 0xe8, 0x95, 0xe5, 0xc5, 0xba, 0x15, 0x3c, 0xbb, 0x62, 0xdd, 0x56, 0x1a, 0x42, 0x7b, 0xad, 0x0f, 0xfd, 0x41, 0x92, 0x31, 0x99},
+ {0xf8, 0xfe, 0xf0, 0x5a, 0x3f, 0xa5, 0xc9, 0xf3, 0xeb, 0xa4, 0x16, 0x38, 0xb2, 0x47, 0xb7, 0x11, 0xa9, 0x9f, 0x96, 0x0f, 0xe7, 0x3a, 0xa2, 0xf9, 0x01, 0x36, 0xae, 0xb2, 0x03, 0x29, 0xb8, 0x88}};
+
+ //Debug printing for the above types
+ //Actually use DP(value) and #define DBG
+ void dp(key a);
+ void dp(bool a);
+ void dp(const char * a, int l);
+ void dp(keyV a);
+ void dp(keyM a);
+ void dp(xmr_amount vali);
+ void dp(int vali);
+ void dp(bits amountb);
+ void dp(const char * st);
+
+ //various conversions
+
+ //uint long long to 32 byte key
+ void d2h(key & amounth, xmr_amount val);
+ key d2h(xmr_amount val);
+ //uint long long to int[64]
+ void d2b(bits amountb, xmr_amount val);
+ //32 byte key to uint long long
+ // if the key holds a value > 2^64
+ // then the value in the first 8 bytes is returned
+ xmr_amount h2d(const key &test);
+ //32 byte key to int[64]
+ void h2b(bits amountb2, key & test);
+ //int[64] to 32 byte key
+ void b2h(key & amountdh, bits amountb2);
+ //int[64] to uint long long
+ xmr_amount b2d(bits amountb);
+
+ static inline const rct::key pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
+ static inline const rct::key sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }
+ static inline const rct::key ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; }
+ static inline const rct::key hash2rct(const crypto::hash &h) { return (const rct::key&)h; }
+ static inline const crypto::public_key rct2pk(const rct::key &k) { return (const crypto::public_key&)k; }
+ static inline const crypto::secret_key rct2sk(const rct::key &k) { return (const crypto::secret_key&)k; }
+ static inline const crypto::key_image rct2ki(const rct::key &k) { return (const crypto::key_image&)k; }
+ static inline const crypto::hash rct2hash(const rct::key &k) { return (const crypto::hash&)k; }
+ static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !memcmp(&k0, &k1, 32); }
+ static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return memcmp(&k0, &k1, 32); }
+}
+
+
+namespace cryptonote {
+ static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); }
+ static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
+ static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); }
+ static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
+}
+
+template<typename T> std::ostream &print256(std::ostream &o, const T &v);
+inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); }
+
+
+BLOB_SERIALIZER(rct::key);
+BLOB_SERIALIZER(rct::key64);
+BLOB_SERIALIZER(rct::ctkey);
+BLOB_SERIALIZER(rct::asnlSig);
+
+VARIANT_TAG(debug_archive, rct::key, "rct::key");
+VARIANT_TAG(debug_archive, rct::key64, "rct::key64");
+VARIANT_TAG(debug_archive, rct::keyV, "rct::keyV");
+VARIANT_TAG(debug_archive, rct::keyM, "rct::keyM");
+VARIANT_TAG(debug_archive, rct::ctkey, "rct::ctkey");
+VARIANT_TAG(debug_archive, rct::ctkeyV, "rct::ctkeyV");
+VARIANT_TAG(debug_archive, rct::ctkeyM, "rct::ctkeyM");
+VARIANT_TAG(debug_archive, rct::ecdhTuple, "rct::ecdhTuple");
+VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig");
+VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig");
+VARIANT_TAG(debug_archive, rct::asnlSig, "rct::asnlSig");
+VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig");
+
+VARIANT_TAG(binary_archive, rct::key, 0x90);
+VARIANT_TAG(binary_archive, rct::key64, 0x91);
+VARIANT_TAG(binary_archive, rct::keyV, 0x92);
+VARIANT_TAG(binary_archive, rct::keyM, 0x93);
+VARIANT_TAG(binary_archive, rct::ctkey, 0x94);
+VARIANT_TAG(binary_archive, rct::ctkeyV, 0x95);
+VARIANT_TAG(binary_archive, rct::ctkeyM, 0x96);
+VARIANT_TAG(binary_archive, rct::ecdhTuple, 0x97);
+VARIANT_TAG(binary_archive, rct::mgSig, 0x98);
+VARIANT_TAG(binary_archive, rct::rangeSig, 0x99);
+VARIANT_TAG(binary_archive, rct::asnlSig, 0x9a);
+VARIANT_TAG(binary_archive, rct::rctSig, 0x9b);
+
+VARIANT_TAG(json_archive, rct::key, "rct_key");
+VARIANT_TAG(json_archive, rct::key64, "rct_key64");
+VARIANT_TAG(json_archive, rct::keyV, "rct_keyV");
+VARIANT_TAG(json_archive, rct::keyM, "rct_keyM");
+VARIANT_TAG(json_archive, rct::ctkey, "rct_ctkey");
+VARIANT_TAG(json_archive, rct::ctkeyV, "rct_ctkeyV");
+VARIANT_TAG(json_archive, rct::ctkeyM, "rct_ctkeyM");
+VARIANT_TAG(json_archive, rct::ecdhTuple, "rct_ecdhTuple");
+VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig");
+VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig");
+VARIANT_TAG(json_archive, rct::asnlSig, "rct_asnlSig");
+VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig");
+
+#endif /* RCTTYPES_H */
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index e6fc11060..5aa3591ab 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -159,9 +159,25 @@ namespace cryptonote
{
res.blocks.resize(res.blocks.size()+1);
res.blocks.back().block = block_to_blob(b.first);
+ res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
+ res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
+ bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.first.miner_tx), res.output_indices.back().indices.back().indices);
+ if (!r)
+ {
+ res.status = "Failed";
+ return false;
+ }
+ size_t txidx = 0;
BOOST_FOREACH(auto& t, b.second)
{
res.blocks.back().txs.push_back(tx_to_blob(t));
+ res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
+ bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices);
+ if (!r)
+ {
+ res.status = "Failed";
+ return false;
+ }
}
}
@@ -250,6 +266,30 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res)
+ {
+ CHECK_CORE_BUSY();
+ res.status = "Failed";
+ if(!m_core.get_random_rct_outs(req, res))
+ {
+ return true;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ std::stringstream ss;
+ typedef COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry out_entry;
+ CHECK_AND_ASSERT_MES(res.outs.size(), true, "internal error: res.outs.size() is empty");
+ std::for_each(res.outs.begin(), res.outs.end(), [&](out_entry& oe)
+ {
+ ss << oe.global_amount_index << " ";
+ });
+ ss << ENDL;
+ std::string s = ss.str();
+ LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS: " << ENDL << s);
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res)
{
CHECK_CORE_BUSY();
@@ -459,6 +499,8 @@ namespace cryptonote
res.reason = "overspend";
if ((res.fee_too_low = tvc.m_fee_too_low))
res.reason = "fee too low";
+ if ((res.not_rct = tvc.m_not_rct))
+ res.reason = "tx is not ringct";
return true;
}
@@ -1023,7 +1065,6 @@ namespace cryptonote
return false;
}
-#if BLOCKCHAIN_DB == DB_LMDB
const Blockchain &blockchain = m_core.get_blockchain_storage();
uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version();
res.version = blockchain.get_current_hard_fork_version();
@@ -1031,11 +1072,6 @@ namespace cryptonote
res.state = blockchain.get_hard_fork_state();
res.status = CORE_RPC_STATUS_OK;
return true;
-#else
- error_resp.code = CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC;
- error_resp.message = "Hard fork inoperative in memory mode.";
- return false;
-#endif
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp)
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 6ebf41abc..9fcf3abf8 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -79,6 +79,7 @@ namespace cryptonote
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
+ MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS)
MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS)
MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT)
MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX)
@@ -128,6 +129,7 @@ namespace cryptonote
bool on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res);
bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res);
+ bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res);
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 22c3590e1..8cba53943 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -41,7 +41,7 @@ namespace cryptonote
#define CORE_RPC_STATUS_BUSY "BUSY"
#define CORE_RPC_STATUS_NOT_MINING "NOT MINING"
-#define CORE_RPC_VERSION 2
+#define CORE_RPC_VERSION 3
struct COMMAND_RPC_GET_HEIGHT
{
@@ -76,18 +76,38 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
+ struct tx_output_indices
+ {
+ std::vector<uint64_t> indices;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(indices)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct block_output_indices
+ {
+ std::vector<tx_output_indices> indices;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(indices)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct response
{
std::list<block_complete_entry> blocks;
uint64_t start_height;
uint64_t current_height;
std::string status;
+ std::vector<block_output_indices> output_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(blocks)
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
KV_SERIALIZE(status)
+ KV_SERIALIZE(output_indices)
END_KV_SERIALIZE_MAP()
};
};
@@ -296,10 +316,12 @@ namespace cryptonote
struct outkey
{
crypto::public_key key;
+ rct::key mask;
bool unlocked;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_VAL_POD_AS_BLOB(key)
+ KV_SERIALIZE_VAL_POD_AS_BLOB(mask)
KV_SERIALIZE(unlocked)
END_KV_SERIALIZE_MAP()
};
@@ -315,6 +337,37 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
+
+ struct COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS
+ {
+ struct request
+ {
+ uint64_t outs_count;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(outs_count)
+ END_KV_SERIALIZE_MAP()
+ };
+
+#pragma pack (push, 1)
+ struct out_entry
+ {
+ uint64_t amount;
+ uint64_t global_amount_index;
+ crypto::public_key out_key;
+ rct::key commitment;
+ };
+#pragma pack(pop)
+
+ struct response
+ {
+ std::list<out_entry> outs;
+ std::string status;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs)
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
//-----------------------------------------------
struct COMMAND_RPC_SEND_RAW_TX
{
@@ -345,6 +398,7 @@ namespace cryptonote
bool too_big;
bool overspend;
bool fee_too_low;
+ bool not_rct;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -357,6 +411,7 @@ namespace cryptonote
KV_SERIALIZE(too_big)
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
+ KV_SERIALIZE(not_rct)
END_KV_SERIALIZE_MAP()
};
};
diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h
index 3644b0e03..9a7e89c49 100644
--- a/src/serialization/crypto.h
+++ b/src/serialization/crypto.h
@@ -92,3 +92,4 @@ VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key");
VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation");
VARIANT_TAG(debug_archive, crypto::key_image, "key_image");
VARIANT_TAG(debug_archive, crypto::signature, "signature");
+
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 8e277a46a..177cdf33a 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -102,6 +102,12 @@ inline bool do_serialize(Archive &ar, T &v)
{
return ::serializer<Archive, T>::serialize(ar, v);
}
+template <class Archive>
+inline bool do_serialize(Archive &ar, bool &v)
+{
+ ar.serialize_blob(&v, sizeof(v));
+ return true;
+}
// Never used in the code base
// #ifndef __GNUC__
diff --git a/src/serialization/vector.h b/src/serialization/vector.h
index a2a69f841..7f2bc78ba 100644
--- a/src/serialization/vector.h
+++ b/src/serialization/vector.h
@@ -32,6 +32,11 @@
#include "serialization.h"
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::vector<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::vector<T> &v);
+
namespace serialization
{
namespace detail
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 1840e54c9..44b5b0c70 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -58,6 +58,7 @@
#include "mnemonics/electrum-words.h"
#include "rapidjson/document.h"
#include "common/json_util.h"
+#include "ringct/rctSigs.h"
#include <stdexcept>
#if defined(WIN32)
@@ -99,6 +100,11 @@ typedef cryptonote::simple_wallet sw;
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
})
+enum TransferType {
+ TransferOriginal,
+ TransferNew,
+};
+
namespace
{
const command_line::arg_descriptor<std::string> arg_wallet_file = {"wallet-file", sw::tr("Use wallet <arg>"), ""};
@@ -642,8 +648,8 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability"));
m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]"));
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height"));
- m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 0 to maximum available)"));
- m_cmd_binder.set_handler("transfer_new", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer, but using a new transaction building algorithm"));
+ m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 0 to maximum available)"));
+ m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm"));
m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0"));
m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address"));
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log <level> - Change current log detail level, <0-4>"));
@@ -973,22 +979,22 @@ bool simple_wallet::generate_from_json(const boost::program_options::variables_m
return false;
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true, 0);
const int current_version = 1;
if (field_version > current_version) {
fail_msg_writer() << boost::format(tr("Version %u too new, we can only grok up to %u")) % field_version % current_version;
return false;
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true, std::string());
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false, 0);
bool recover = field_scan_from_height_found;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false, std::string());
password = field_password;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false, std::string());
crypto::secret_key viewkey;
if (field_viewkey_found)
{
@@ -1006,7 +1012,7 @@ bool simple_wallet::generate_from_json(const boost::program_options::variables_m
}
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false, std::string());
crypto::secret_key spendkey;
if (field_spendkey_found)
{
@@ -1024,7 +1030,7 @@ bool simple_wallet::generate_from_json(const boost::program_options::variables_m
}
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false, std::string());
std::string old_language;
if (field_seed_found)
{
@@ -1037,7 +1043,7 @@ bool simple_wallet::generate_from_json(const boost::program_options::variables_m
m_restore_deterministic_wallet = true;
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
// compatibility checks
if (!field_seed_found && !field_viewkey_found)
@@ -1888,24 +1894,24 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index)
+void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
{
message_writer(epee::log_space::console_color_green, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("transaction ") << get_transaction_hash(tx) << ", " <<
- tr("received ") << print_money(tx.vout[out_index].amount);
+ tr("received ") << print_money(amount);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
else
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx)
+void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx)
{
message_writer(epee::log_space::console_color_magenta, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("transaction ") << get_transaction_hash(spend_tx) << ", " <<
- tr("spent ") << print_money(in_tx.vout[out_index].amount);
+ tr("spent ") << print_money(amount);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
else
@@ -2052,15 +2058,17 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
{
if (!transfers_found)
{
- message_writer() << boost::format("%21s%8s%16s%68s") % tr("amount") % tr("spent") % tr("global index") % tr("tx id");
+ message_writer() << boost::format("%21s%8s%12s%8s%16s%68s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id");
transfers_found = true;
}
message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
- boost::format("%21s%8s%16u%68s") %
+ boost::format("%21s%8s%12s%8s%16u%68s") %
print_money(td.amount()) %
(td.m_spent ? tr("T") : tr("F")) %
+ (m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) %
+ (td.is_rct() ? tr("RingCT") : tr("-")) %
td.m_global_output_index %
- get_transaction_hash (td.m_tx);
+ td.m_txid;
}
}
@@ -2274,7 +2282,7 @@ bool simple_wallet::get_address_from_str(const std::string &str, cryptonote::acc
return true;
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::string> &args_)
+bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
{
if (!try_connect_to_daemon())
return true;
@@ -2386,10 +2394,17 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
{
// figure out what tx will be necessary
std::vector<tools::wallet2::pending_tx> ptx_vector;
- if (new_algorithm)
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
- else
- ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ switch (transfer_type)
+ {
+ case TransferNew:
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ break;
+ default:
+ LOG_ERROR("Unknown transfer method, using original");
+ case TransferOriginal:
+ ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ break;
+ }
// if more than one tx necessary, prompt user to confirm
if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
@@ -2531,14 +2546,13 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer(const std::vector<std::string> &args_)
{
- return transfer_main(false, args_);
+ return transfer_main(TransferOriginal, args_);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_new(const std::vector<std::string> &args_)
{
- return transfer_main(true, args_);
+ return transfer_main(TransferNew, args_);
}
-
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
@@ -2944,15 +2958,15 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
LOCK_IDLE_SCOPE();
crypto::secret_key tx_key;
- bool r = m_wallet->get_tx_key(txid, tx_key);
- if (r)
+ std::vector<crypto::secret_key> amount_keys;
+ if (m_wallet->get_tx_key(txid, tx_key))
{
- success_msg_writer() << tr("Tx key: ") << tx_key;
+ success_msg_writer() << tr("Tx key: ") << epee::string_tools::pod_to_hex(tx_key);
return true;
}
else
{
- fail_msg_writer() << tr("no tx key found for this txid");
+ fail_msg_writer() << tr("no tx keys found for this txid");
return true;
}
}
@@ -2979,13 +2993,19 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
LOCK_IDLE_SCOPE();
+ if (local_args[1].size() < 64 || local_args[1].size() % 64)
+ {
+ fail_msg_writer() << tr("failed to parse tx key");
+ return true;
+ }
+ crypto::secret_key tx_key;
cryptonote::blobdata tx_key_data;
if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data))
{
fail_msg_writer() << tr("failed to parse tx key");
return true;
}
- crypto::secret_key tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
+ tx_key = *reinterpret_cast<const crypto::secret_key*>(tx_key_data.data());
cryptonote::account_public_address address;
bool has_payment_id;
@@ -3000,14 +3020,14 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
COMMAND_RPC_GET_TRANSACTIONS::response res;
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
if (!net_utils::invoke_http_json_remote_command2(m_daemon_address + "/gettransactions", req, res, m_http_client) ||
- (res.txs.empty() && res.txs_as_hex.empty()))
+ (res.txs.size() != 1 && res.txs_as_hex.size() != 1))
{
fail_msg_writer() << tr("failed to get transaction from daemon");
return true;
}
cryptonote::blobdata tx_data;
bool ok;
- if (!res.txs.empty())
+ if (res.txs.size() == 1)
ok = string_tools::parse_hexstr_to_binbuff(res.txs.front().as_hex, tx_data);
else
ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
@@ -3046,13 +3066,49 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
crypto::public_key pubkey;
derive_public_key(derivation, n, address.m_spend_public_key, pubkey);
if (pubkey == tx_out_to_key.key)
- received += tx.vout[n].amount;
+ {
+ uint64_t amount;
+ if (tx.version == 1)
+ {
+ amount = tx.vout[n].amount;
+ }
+ else
+ {
+ try
+ {
+ rct::key Ctmp;
+ //rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key)));
+ crypto::key_derivation derivation;
+ bool r = crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation);
+ if (!r)
+ {
+ LOG_ERROR("Failed to generate key derivation to decode rct output " << n);
+ amount = 0;
+ }
+ else
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, n, scalar1);
+ rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
+ rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1));
+ rct::key C = tx.rct_signatures.outPk[n].mask;
+ rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
+ if (rct::equalKeys(C, Ctmp))
+ amount = rct::h2d(ecdh_info.amount);
+ else
+ amount = 0;
+ }
+ }
+ catch (...) { amount = 0; }
+ }
+ received += amount;
+ }
}
}
- catch(...)
+ catch(const std::exception &e)
{
- LOG_ERROR("unknown error");
- fail_msg_writer() << tr("unknown error");
+ LOG_ERROR("error: " << e.what());
+ fail_msg_writer() << tr("error: ") << e.what();
return true;
}
@@ -3064,6 +3120,24 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
{
fail_msg_writer() << get_account_address_as_str(m_wallet->testnet(), address) << " " << tr("received nothing in txid") << " " << txid;
}
+ if (res.txs.front().in_pool)
+ {
+ success_msg_writer() << tr("WARNING: this transaction is not yet included in the blockchain!");
+ }
+ else
+ {
+ std::string err;
+ uint64_t bc_height = get_daemon_blockchain_height(err);
+ if (err.empty())
+ {
+ uint64_t confirmations = bc_height - (res.txs.front().block_height + 1);
+ success_msg_writer() << boost::format(tr("This transaction has %u confirmations")) % confirmations;
+ }
+ else
+ {
+ success_msg_writer() << tr("WARNING: failed to determine number of confirmations!");
+ }
+ }
return true;
}
@@ -3228,9 +3302,8 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
m_wallet->get_unconfirmed_payments_out(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
- uint64_t amount = 0;
- cryptonote::get_inputs_money_amount(pd.m_tx, amount);
- uint64_t fee = amount - get_outs_money_amount(pd.m_tx);
+ uint64_t amount = pd.m_amount_in;
+ uint64_t fee = amount - pd.m_amount_out;
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 52a4e150f..1259b95b0 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -120,7 +120,7 @@ namespace cryptonote
bool show_incoming_transfers(const std::vector<std::string> &args);
bool show_payments(const std::vector<std::string> &args);
bool show_blockchain_height(const std::vector<std::string> &args);
- bool transfer_main(bool new_algorithm, const std::vector<std::string> &args);
+ bool transfer_main(int transfer_type, const std::vector<std::string> &args);
bool transfer(const std::vector<std::string> &args);
bool transfer_new(const std::vector<std::string> &args);
bool sweep_all(const std::vector<std::string> &args);
@@ -171,8 +171,8 @@ namespace cryptonote
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index);
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx);
+ virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount);
+ virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx);
virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx);
//----------------------------------------------------------
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index db42e2141..95aafb04f 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -165,9 +165,8 @@ void TransactionHistoryImpl::refresh()
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
const crypto::hash &hash = i->first;
- uint64_t amount = 0;
- cryptonote::get_inputs_money_amount(pd.m_tx, amount);
- uint64_t fee = amount - get_outs_money_amount(pd.m_tx);
+ uint64_t amount = pd.m_amount_in;
+ uint64_t fee = amount - pd.m_amount_out;
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index b3ee4112b..f8704fde3 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -79,11 +79,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
LOG_PRINT_L3(__FUNCTION__ << ": new block. height: " << height);
}
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index)
+ virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
{
std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx));
- uint64_t amount = tx.vout[out_index].amount;
LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height
<< ", tx: " << tx_hash
@@ -94,12 +93,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index,
+ virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount,
const cryptonote::transaction& spend_tx)
{
// TODO;
std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(spend_tx));
- uint64_t amount = in_tx.vout[out_index].amount;
LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
@@ -491,12 +489,6 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
}
de.amount = amount;
- if (de.amount <= 0) {
- m_status = Status_Error;
- m_errorString = "Invalid amount";
- break;
- }
-
dsts.push_back(de);
//std::vector<tools::wallet2::pending_tx> ptx_vector;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 8502353ed..99866dbeb 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <random>
+#include <tuple>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
@@ -55,6 +56,7 @@ using namespace epee;
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
#include "common/base58.h"
+#include "ringct/rctSigs.h"
extern "C"
{
@@ -96,14 +98,18 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
}
}
-uint64_t calculate_fee(const cryptonote::blobdata &blob, uint64_t fee_multiplier)
+uint64_t calculate_fee(size_t bytes, uint64_t fee_multiplier)
{
THROW_WALLET_EXCEPTION_IF(fee_multiplier <= 0 || fee_multiplier > 3, tools::error::invalid_fee_multiplier);
- uint64_t bytes = blob.size();
uint64_t kB = (bytes + 1023) / 1024;
return kB * FEE_PER_KB * fee_multiplier;
}
+uint64_t calculate_fee(const cryptonote::blobdata &blob, uint64_t fee_multiplier)
+{
+ return calculate_fee(blob.size(), fee_multiplier);
+}
+
} //namespace
namespace tools
@@ -168,7 +174,21 @@ bool wallet2::is_deprecated() const
return is_old_file_format;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const crypto::public_key &tx_pub_key, size_t i, uint64_t &money_transfered, bool &error) const
+void wallet2::set_spent(transfer_details &td, uint64_t height)
+{
+ LOG_PRINT_L2("Setting SPENT at " << height << ": ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
+ td.m_spent = true;
+ td.m_spent_height = height;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::set_unspent(transfer_details &td)
+{
+ LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
+ td.m_spent = false;
+ td.m_spent_height = 0;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
{
if (o.target.type() != typeid(txout_to_key))
{
@@ -176,9 +196,10 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp
LOG_ERROR("wrong type id in transaction out");
return;
}
- if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i))
+ received = is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i);
+ if(received)
{
- money_transfered = o.amount;
+ money_transfered = o.amount; // may be 0 for ringct outputs
}
else
{
@@ -187,8 +208,57 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp
error = false;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+static uint64_t decodeRct(const rct::rctSig & rv, const crypto::public_key pub, const crypto::secret_key &sec, unsigned int i, rct::key & mask)
{
+ crypto::key_derivation derivation;
+ bool r = crypto::generate_key_derivation(pub, sec, derivation);
+ if (!r)
+ {
+ LOG_ERROR("Failed to generate key derivation to decode rct output " << i);
+ return 0;
+ }
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, i, scalar1);
+ try
+ {
+ switch (rv.type)
+ {
+ case rct::RCTTypeSimple:
+ return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask);
+ case rct::RCTTypeFull:
+ return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask);
+ default:
+ LOG_ERROR("Unsupported rct type: " << rv.type);
+ return 0;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR("Failed to decode input " << i);
+ return 0;
+ }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+{
+ class lazy_txid_getter
+ {
+ const cryptonote::transaction &tx;
+ crypto::hash lazy_txid;
+ bool computed;
+ public:
+ lazy_txid_getter(const transaction &tx): tx(tx), computed(false) {}
+ const crypto::hash &operator()()
+ {
+ if (!computed)
+ {
+ lazy_txid = cryptonote::get_transaction_hash(tx);
+ computed = true;
+ }
+ return lazy_txid;
+ }
+ } txid(tx);
+
if (!miner_tx)
process_unconfirmed(tx, height);
std::vector<size_t> outs;
@@ -199,7 +269,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
if(!parse_tx_extra(tx.extra, tx_extra_fields))
{
// Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
- LOG_PRINT_L0("Transaction extra has unsupported format: " << get_transaction_hash(tx));
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid());
}
// Don't try to extract tx public key if tx has no ouputs
@@ -208,14 +278,19 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
tx_extra_pub_key pub_key_field;
if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field))
{
- LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << get_transaction_hash(tx));
+ LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid());
if(0 != m_callback)
m_callback->on_skip_transaction(height, tx);
return;
}
+ int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key;
bool r = true;
+ std::deque<cryptonote::keypair> in_ephemeral(tx.vout.size());
+ std::deque<crypto::key_image> ki(tx.vout.size());
+ std::deque<uint64_t> amount(tx.vout.size());
+ std::deque<rct::key> mask(tx.vout.size());
int threads = tools::get_max_concurrency();
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
{
@@ -224,8 +299,8 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{
uint64_t money_transfered = 0;
- bool error = false;
- check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, money_transfered, error);
+ bool error = false, received = false;
+ check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, received, money_transfered, error);
if (error)
{
r = false;
@@ -233,10 +308,21 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
else
{
// this assumes that the miner tx pays a single address
- if (money_transfered > 0)
+ if (received)
{
+ cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]);
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
outs.push_back(0);
+ if (money_transfered == 0)
+ {
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, 0, mask[0]);
+ }
+ amount[0] = money_transfered;
tx_money_got_in_outs = money_transfered;
+ ++num_vouts_received;
// process the other outs from that tx
boost::asio::io_service ioservice;
@@ -250,11 +336,12 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
const account_keys &keys = m_account.get_keys();
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
+ std::deque<bool> received(tx.vout.size());
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
- std::ref(money_transfered[i]), std::ref(error[i])));
+ std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
KILL_IOSERVICE();
for (size_t i = 1; i < tx.vout.size(); ++i)
@@ -264,10 +351,21 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
r = false;
break;
}
- if (money_transfered[i])
+ if (received[i])
{
+ cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
outs.push_back(i);
+ if (money_transfered[i] == 0)
+ {
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
+ }
tx_money_got_in_outs += money_transfered[i];
+ amount[i] = money_transfered[i];
+ ++num_vouts_received;
}
}
}
@@ -286,10 +384,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
const account_keys &keys = m_account.get_keys();
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
+ std::deque<bool> received(tx.vout.size());
for (size_t i = 0; i < tx.vout.size(); ++i)
{
ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
- std::ref(money_transfered[i]), std::ref(error[i])));
+ std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
KILL_IOSERVICE();
tx_money_got_in_outs = 0;
@@ -300,37 +399,68 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
r = false;
break;
}
- if (money_transfered[i])
+ if (received[i])
{
+ cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
outs.push_back(i);
+ if (money_transfered[i] == 0)
+ {
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ money_transfered[i] = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
+ }
tx_money_got_in_outs += money_transfered[i];
+ amount[i] = money_transfered[i];
+ ++num_vouts_received;
}
}
}
else
{
- r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs);
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ {
+ uint64_t money_transfered = 0;
+ bool error = false, received = false;
+ check_acc_out(m_account.get_keys(), tx.vout[i], tx_pub_key, i, received, money_transfered, error);
+ if (error)
+ {
+ r = false;
+ break;
+ }
+ else
+ {
+ if (received)
+ {
+ cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
+ THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
+ error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
+
+ outs.push_back(i);
+ if (money_transfered == 0)
+ {
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ money_transfered = tools::decodeRct(tx.rct_signatures, pub_key_field.pub_key, keys.m_view_secret_key, i, mask[i]);
+ }
+ amount[i] = money_transfered;
+ tx_money_got_in_outs += money_transfered;
+ ++num_vouts_received;
+ }
+ }
+ }
}
THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
- if(!outs.empty() && tx_money_got_in_outs)
+ if(!outs.empty() && num_vouts_received > 0)
{
//good news - got money! take care about it
//usually we have only one transfer for user in transaction
- cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req);
- cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res);
if (!pool)
{
- req.txid = get_transaction_hash(tx);
- m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
- THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
- "transactions outputs size=" + std::to_string(tx.vout.size()) +
- " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));
+ THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error,
+ "transactions outputs size=" + std::to_string(tx.vout.size()) +
+ " not match with daemon response size=" + std::to_string(o_indices.size()));
}
BOOST_FOREACH(size_t o, outs)
@@ -338,13 +468,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
- crypto::key_image ki;
- cryptonote::keypair in_ephemeral;
- cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, ki);
- THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key,
- error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
-
- auto kit = m_key_images.find(ki);
+ auto kit = m_key_images.find(ki[o]);
THROW_WALLET_EXCEPTION_IF(kit != m_key_images.end() && kit->second >= m_transfers.size(),
error::wallet_internal_error, std::string("Unexpected transfer index from key image: ")
+ "got " + (kit == m_key_images.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
@@ -357,14 +481,32 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
- td.m_global_output_index = res.o_indexes[o];
- td.m_tx = tx;
- td.m_key_image = ki;
- td.m_spent = false;
+ td.m_global_output_index = o_indices[o];
+ td.m_tx = (const cryptonote::transaction_prefix&)tx;
+ td.m_txid = txid();
+ td.m_key_image = ki[o];
+ td.m_amount = tx.vout[o].amount;
+ if (td.m_amount == 0)
+ {
+ td.m_mask = mask[o];
+ td.m_amount = amount[o];
+ td.m_rct = true;
+ }
+ else if (miner_tx && tx.version == 2)
+ {
+ td.m_mask = rct::identity();
+ td.m_rct = true;
+ }
+ else
+ {
+ td.m_mask = rct::identity();
+ td.m_rct = false;
+ }
+ set_unspent(td);
m_key_images[td.m_key_image] = m_transfers.size()-1;
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
if (0 != m_callback)
- m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ m_callback->on_money_received(height, tx, td.m_amount);
}
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
@@ -387,14 +529,32 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
transfer_details &td = m_transfers[kit->second];
td.m_block_height = height;
td.m_internal_output_index = o;
- td.m_global_output_index = res.o_indexes[o];
- td.m_tx = tx;
- THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki, error::wallet_internal_error, "Inconsistent key images");
+ td.m_global_output_index = o_indices[o];
+ td.m_tx = (const cryptonote::transaction_prefix&)tx;
+ td.m_txid = txid();
+ td.m_amount = tx.vout[o].amount;
+ if (td.m_amount == 0)
+ {
+ td.m_mask = mask[o];
+ td.m_amount = amount[o];
+ td.m_rct = true;
+ }
+ else if (miner_tx && tx.version == 2)
+ {
+ td.m_mask = rct::identity();
+ td.m_rct = true;
+ }
+ else
+ {
+ td.m_mask = rct::identity();
+ td.m_rct = false;
+ }
+ THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki[o], error::wallet_internal_error, "Inconsistent key images");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
if (0 != m_callback)
- m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
+ m_callback->on_money_received(height, tx, td.m_amount);
}
}
}
@@ -410,12 +570,20 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
if(it != m_key_images.end())
{
- LOG_PRINT_L0("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx));
- tx_money_spent_in_ins += boost::get<cryptonote::txin_to_key>(in).amount;
transfer_details& td = m_transfers[it->second];
- td.m_spent = true;
+ uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
+ if (amount > 0)
+ {
+ THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
+ std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
+ std::string(", expected ") + print_money(td.amount()));
+ }
+ amount = td.amount();
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid());
+ tx_money_spent_in_ins += amount;
+ set_spent(td, height);
if (0 != m_callback)
- m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx);
+ m_callback->on_money_spent(height, tx, amount, tx);
}
}
@@ -467,7 +635,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
}
payment_details payment;
- payment.m_tx_hash = cryptonote::get_transaction_hash(tx);
+ payment.m_tx_hash = txid();
payment.m_amount = received;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
@@ -482,6 +650,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
//----------------------------------------------------------------------------------------------------
void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height)
{
+ if (m_unconfirmed_txs.empty())
+ return;
+
crypto::hash txid = get_transaction_hash(tx);
auto unconf_it = m_unconfirmed_txs.find(txid);
if(unconf_it != m_unconfirmed_txs.end()) {
@@ -501,25 +672,38 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he
void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
{
crypto::hash txid = get_transaction_hash(tx);
- confirmed_transfer_details &ctd = m_confirmed_txs[txid];
- // operator[] creates if not found
+ std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
// fill with the info we know, some info might already be there
- ctd.m_amount_in = spent;
- ctd.m_amount_out = get_outs_money_amount(tx);
- ctd.m_change = received;
- ctd.m_block_height = height;
- ctd.m_timestamp = ts;
+ if (entry.second)
+ {
+ // this case will happen if the tx is from our outputs, but was sent by another
+ // wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
+ // we only see 0 input amounts, so have to deduce amount out from other parameters.
+ entry.first->second.m_amount_in = spent;
+ if (tx.version == 1)
+ entry.first->second.m_amount_out = get_outs_money_amount(tx);
+ else
+ entry.first->second.m_amount_out = spent - tx.rct_signatures.txnFee;
+ entry.first->second.m_change = received;
+ }
+ entry.first->second.m_block_height = height;
+ entry.first->second.m_timestamp = ts;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height)
+void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices)
{
+ size_t txidx = 0;
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() + 1 != o_indices.indices.size(), error::wallet_internal_error,
+ "block transactions=" + std::to_string(bche.txs.size()) +
+ " not match with daemon response size=" + std::to_string(o_indices.indices.size()));
+
//handle transactions from new block
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(b.miner_tx, height, b.timestamp, true, false);
+ process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
@@ -528,7 +712,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
cryptonote::transaction tx;
bool r = parse_and_validate_tx_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(tx, height, b.timestamp, false, false);
+ process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
}
TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
@@ -578,7 +762,7 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl
bl_id = get_block_hash(bl);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks)
+void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
{
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
@@ -591,9 +775,13 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status);
+ THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
+ "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
+ boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
blocks_start_height = res.start_height;
blocks = res.blocks;
+ o_indices = res.output_indices;
}
//----------------------------------------------------------------------------------------------------
void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes)
@@ -614,10 +802,13 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
hashes = res.m_block_ids;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added)
+void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added)
{
size_t current_index = start_height;
blocks_added = 0;
+ size_t tx_o_indices_idx = 0;
+
+ THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
int threads = tools::get_max_concurrency();
if (threads > 1)
@@ -660,7 +851,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
if(current_index >= m_blockchain.size())
{
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index);
+ process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
++blocks_added;
}
else if(bl_id != m_blockchain[current_index])
@@ -672,7 +863,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index);
- process_new_blockchain_entry(bl, *blocki, bl_id, current_index);
+ process_new_blockchain_entry(bl, *blocki, bl_id, current_index, o_indices[b+i]);
}
else
{
@@ -694,7 +885,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
crypto::hash bl_id = get_block_hash(bl);
if(current_index >= m_blockchain.size())
{
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
+ process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
++blocks_added;
}
else if(bl_id != m_blockchain[current_index])
@@ -706,7 +897,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index);
- process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
+ process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, o_indices[tx_o_indices_idx]);
}
else
{
@@ -714,6 +905,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
}
++current_index;
+ ++tx_o_indices_idx;
}
}
}
@@ -730,7 +922,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched)
refresh(start_height, blocks_fetched, received_money);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, bool &error)
+void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error)
{
error = false;
@@ -748,7 +940,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
}
// pull the new blocks
- pull_blocks(start_height, blocks_start_height, short_chain_history, blocks);
+ pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
}
catch(...)
{
@@ -772,7 +964,7 @@ void wallet2::update_pool_state()
std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
while (it != m_unconfirmed_txs.end())
{
- const std::string txid = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(it->second.m_tx));
+ const std::string txid = epee::string_tools::pod_to_hex(it->first);
bool found = false;
for (auto it2: res.transactions)
{
@@ -799,6 +991,24 @@ void wallet2::update_pool_state()
{
LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed");
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
+
+ // the inputs aren't spent anymore, since the tx failed
+ for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
+ {
+ if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
+ {
+ txin_to_key &tx_in_to_key = boost::get<txin_to_key>(pit->second.m_tx.vin[vini]);
+ for (auto &td: m_transfers)
+ {
+ if (td.m_key_image == tx_in_to_key.k_image)
+ {
+ LOG_PRINT_L1("Resetting spent status for output " << vini << ": " << td.m_key_image);
+ set_unspent(td);
+ break;
+ }
+ }
+ }
+ }
}
}
}
@@ -869,7 +1079,7 @@ void wallet2::update_pool_state()
{
if (tx_hash == txid)
{
- process_new_transaction(tx, 0, time(NULL), false, true);
+ process_new_transaction(tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
}
else
{
@@ -980,11 +1190,12 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
blocks_fetched = 0;
uint64_t added_blocks = 0;
size_t try_count = 0;
- crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash;
+ crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
boost::thread pull_thread;
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
+ std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
// pull the first set of blocks
get_short_chain_history(short_chain_history);
@@ -1001,7 +1212,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// and then fall through to regular refresh processing
}
- pull_blocks(start_height, blocks_start_height, short_chain_history, blocks);
+ pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
// always reset start_height to 0 to force short_chain_ history to be used on
// subsequent pulls in this refresh.
start_height = 0;
@@ -1013,10 +1224,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// pull the next set of blocks while we're processing the current one
uint64_t next_blocks_start_height;
std::list<cryptonote::block_complete_entry> next_blocks;
+ std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
bool error = false;
- pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, error);});
+ pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
- process_blocks(blocks_start_height, blocks, added_blocks);
+ process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
blocks_fetched += added_blocks;
pull_thread.join();
if(!added_blocks)
@@ -1025,6 +1237,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
// switch to the new blocks from the daemon
blocks_start_height = next_blocks_start_height;
blocks = next_blocks;
+ o_indices = next_o_indices;
// handle error from async fetching thread
if (error)
@@ -1049,7 +1262,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
}
}
}
- if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash))
+ if(last_tx_hash_id != (m_transfers.size() ? m_transfers.back().m_txid : null_hash))
received_money = true;
try
@@ -1083,6 +1296,16 @@ void wallet2::detach_blockchain(uint64_t height)
LOG_PRINT_L0("Detaching blockchain on height " << height);
size_t transfers_detached = 0;
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ wallet2::transfer_details &td = m_transfers[i];
+ if (td.m_spent && td.m_spent_height >= height)
+ {
+ LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image);
+ set_unspent(td);
+ }
+ }
+
auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;});
size_t i_start = it - m_transfers.begin();
@@ -1272,26 +1495,25 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
const char *field_key_data = json["key_data"].GetString();
account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength());
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false, std::string());
if (field_seed_language_found)
{
set_seed_language(field_seed_language);
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false);
- m_watch_only = field_watch_only_found && field_watch_only;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false, false);
+ m_watch_only = field_watch_only;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false, false);
m_always_confirm_transfers = field_always_confirm_transfers_found && field_always_confirm_transfers;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false);
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false);
- m_store_tx_info = (field_store_tx_keys_found && (field_store_tx_keys != 0))
- || (field_store_tx_info_found && (field_store_tx_info != 0));
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false);
- m_default_mixin = field_default_mixin_found ? field_default_mixin : 0;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false);
- m_default_fee_multiplier = field_default_fee_multiplier_found ? field_default_fee_multiplier : 0;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false);
- m_auto_refresh = !field_auto_refresh_found || (field_auto_refresh != 0);
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
+ m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
+ m_default_mixin = field_default_mixin;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_fee_multiplier, unsigned int, Uint, false, 0);
+ m_default_fee_multiplier = field_default_fee_multiplier;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false, true);
+ m_auto_refresh = field_auto_refresh;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false, RefreshType::RefreshDefault);
m_refresh_type = RefreshType::RefreshDefault;
if (field_refresh_type_found)
{
@@ -1300,7 +1522,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
else
LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default");
}
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
if (field_refresh_height_found)
m_refresh_from_block_height = field_refresh_height;
}
@@ -1896,12 +2118,15 @@ void wallet2::rescan_spent()
if (td.m_spent)
{
LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as unspent, it was marked as spent");
+ set_unspent(td);
+ td.m_spent_height = 0;
}
else
{
LOG_PRINT_L0("Marking output " << i << "(" << td.m_key_image << ") as spent, it was marked as unspent");
+ set_spent(td, td.m_spent_height);
+ // unknown height, if this gets reorged, it might still be missed
}
- td.m_spent = daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
}
}
}
@@ -1962,6 +2187,7 @@ namespace
T pop_index(std::vector<T>& vec, size_t idx)
{
CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
+ CHECK_AND_ASSERT_MES(idx < vec.size(), T(), "idx out of bounds");
T res = vec[idx];
if (idx + 1 != vec.size())
@@ -1981,6 +2207,84 @@ namespace
size_t idx = crypto::rand<size_t>() % vec.size();
return pop_index (vec, idx);
}
+
+ template<typename T>
+ T pop_back(std::vector<T>& vec)
+ {
+ CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty");
+
+ T res = vec.back();
+ vec.pop_back();
+ return res;
+ }
+}
+//----------------------------------------------------------------------------------------------------
+// This returns a handwavy estimation of how much two outputs are related
+// If they're from the same tx, then they're fully related. From close block
+// heights, they're kinda related. The actual values don't matter, just
+// their ordering, but it could become more murky if we add scores later.
+float wallet2::get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const
+{
+ int dh;
+
+ // expensive test, and same tx will fall onto the same block height below
+ if (td0.m_txid == td1.m_txid)
+ return 1.0f;
+
+ // same block height -> possibly tx burst, or same tx (since above is disabled)
+ dh = td0.m_block_height > td1.m_block_height ? td0.m_block_height - td1.m_block_height : td1.m_block_height - td0.m_block_height;
+ if (dh == 0)
+ return 0.9f;
+
+ // adjacent blocks -> possibly tx burst
+ if (dh == 1)
+ return 0.8f;
+
+ // could extract the payment id, and compare them, but this is a bit expensive too
+
+ // similar block heights
+ if (dh < 10)
+ return 0.2f;
+
+ // don't think these are particularly related
+ return 0.0f;
+}
+//----------------------------------------------------------------------------------------------------
+size_t wallet2::pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_indices, const std::list<transfer_container::iterator>& selected_transfers) const
+{
+ std::vector<size_t> candidates;
+ float best_relatedness = 1.0f;
+ for (size_t n = 0; n < unused_indices.size(); ++n)
+ {
+ const transfer_details &candidate = transfers[unused_indices[n]];
+ float relatedness = 0.0f;
+ for (const auto &i: selected_transfers)
+ {
+ float r = get_output_relatedness(candidate, *i);
+ if (r > relatedness)
+ {
+ relatedness = r;
+ if (relatedness == 1.0f)
+ break;
+ }
+ }
+
+ if (relatedness < best_relatedness)
+ {
+ best_relatedness = relatedness;
+ candidates.clear();
+ }
+
+ if (relatedness == best_relatedness)
+ candidates.push_back(n);
+ }
+ size_t idx = crypto::rand<size_t>() % candidates.size();
+ return pop_index (unused_indices, candidates[idx]);
+}
+//----------------------------------------------------------------------------------------------------
+size_t wallet2::pop_best_value(std::vector<size_t> &unused_indices, const std::list<transfer_container::iterator>& selected_transfers) const
+{
+ return pop_best_value_from(m_transfers, unused_indices, selected_transfers);
}
//----------------------------------------------------------------------------------------------------
// Select random input sources for transaction.
@@ -1992,7 +2296,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
uint64_t found_money = 0;
while (found_money < needed_money && !unused_transfers_indices.empty())
{
- size_t idx = pop_random_value(unused_transfers_indices);
+ size_t idx = pop_best_value(unused_transfers_indices, selected_transfers);
transfer_container::iterator it = m_transfers.begin() + idx;
selected_transfers.push_back(it);
@@ -2002,12 +2306,16 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
return found_money;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount)
+void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount)
{
unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
+ utd.m_amount_in = amount_in;
+ utd.m_amount_out = 0;
+ for (const auto &d: dests)
+ utd.m_amount_out += d.amount;
utd.m_change = change_amount;
utd.m_sent_time = time(NULL);
- utd.m_tx = tx;
+ utd.m_tx = (const cryptonote::transaction_prefix&)tx;
utd.m_dests = dests;
utd.m_payment_id = payment_id;
utd.m_state = wallet2::unconfirmed_transfer_details::pending;
@@ -2190,19 +2498,26 @@ void wallet2::commit_tx(pending_tx& ptx)
txid = get_transaction_hash(ptx.tx);
crypto::hash payment_id = cryptonote::null_hash;
std::vector<cryptonote::tx_destination_entry> dests;
+ uint64_t amount_in = 0;
if (store_tx_info())
{
payment_id = get_payment_id(ptx);
dests = ptx.dests;
+ BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
+ amount_in += it->amount();
}
- add_unconfirmed_tx(ptx.tx, dests, payment_id, ptx.change_dts.amount);
+ add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount);
if (store_tx_info())
+ {
m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
+ }
LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
- it->m_spent = true;
+ {
+ set_spent(*it, 0);
+ }
//fee includes dust if dust policy specified it.
LOG_PRINT_L0("Transaction successfully sent. <" << txid << ">" << ENDL
@@ -2280,7 +2595,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
// mark transfers to be used as "spent"
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
- it->m_spent = true;
+ {
+ set_spent(*it, 0);
+ }
}
// if we made it this far, we've selected our transactions. committing them will mark them spent,
@@ -2290,7 +2607,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
+ {
+ set_unspent(*it2);
+ }
}
@@ -2307,8 +2626,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
-
+ {
+ set_unspent(*it2);
+ }
}
if (attempt_count >= MAX_SPLIT_ATTEMPTS)
@@ -2325,8 +2645,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
-
+ {
+ set_unspent(*it2);
+ }
}
throw;
@@ -2334,40 +2655,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
}
}
-template<typename T>
-void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
+template<typename entry>
+void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<transfer_container::iterator> &selected_transfers, size_t fake_outputs_count)
{
- using namespace cryptonote;
- // throw if attempting a transaction with no destinations
- THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
-
- uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
- uint64_t needed_money = fee;
- LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
-
- // calculate total amount being sent to all destinations
- // throw if total amount overflows uint64_t
- BOOST_FOREACH(auto& dt, dsts)
- {
- THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
- needed_money += dt.amount;
- LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
- THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet);
- }
-
- uint64_t found_money = 0;
- BOOST_FOREACH(auto it, selected_transfers)
- {
- found_money += it->amount();
- }
-
- LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee));
- THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
-
- typedef std::pair<uint64_t, crypto::public_key> entry;
- std::vector<std::vector<entry>> outs;
- if (fake_outputs_count)
+ LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
+ outs.clear();
+ if (fake_outputs_count > 0)
{
// get histogram for the amounts we need
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t);
@@ -2377,7 +2670,10 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_output_histogram";
for(auto it: selected_transfers)
- req_t.params.amounts.push_back(it->amount());
+ req_t.params.amounts.push_back(it->is_rct() ? 0 : it->amount());
+ std::sort(req_t.params.amounts.begin(), req_t.params.amounts.end());
+ auto end = std::unique(req_t.params.amounts.begin(), req_t.params.amounts.end());
+ req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end));
req_t.params.unlocked = true;
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
m_daemon_rpc_mutex.unlock();
@@ -2387,12 +2683,15 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
// we ask for more, to have spares if some outputs are still locked
size_t requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
+ LOG_PRINT_L2("requested_outputs_count: " << requested_outputs_count);
// generate output indices to request
COMMAND_RPC_GET_OUTPUTS::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
+
for(transfer_container::iterator it: selected_transfers)
{
+ const uint64_t amount = it->is_rct() ? 0 : it->amount();
std::unordered_set<uint64_t> seen_indices;
size_t start = req.outputs.size();
@@ -2401,28 +2700,30 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
uint64_t num_outs = 0;
for (auto he: resp_t.result.histogram)
{
- if (he.amount == it->amount())
+ if (he.amount == amount)
{
num_outs = he.instances;
break;
}
}
+ LOG_PRINT_L1("" << num_outs << " outputs of size " << print_money(amount));
+
if (num_outs <= requested_outputs_count)
{
for (uint64_t i = 0; i < num_outs; i++)
- req.outputs.push_back({it->amount(), i});
+ req.outputs.push_back({amount, i});
// duplicate to make up shortfall: this will be caught after the RPC call,
// so we can also output the amounts for which we can't reach the required
// mixin after checking the actual unlockedness
for (uint64_t i = num_outs; i < requested_outputs_count; ++i)
- req.outputs.push_back({it->amount(), num_outs - 1});
+ req.outputs.push_back({amount, num_outs - 1});
}
else
{
// start with real one
uint64_t num_found = 1;
seen_indices.emplace(it->m_global_output_index);
- req.outputs.push_back({it->amount(), it->m_global_output_index});
+ req.outputs.push_back({amount, it->m_global_output_index});
// while we still need more mixins
while (num_found < requested_outputs_count)
@@ -2447,7 +2748,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
continue;
seen_indices.emplace(i);
- req.outputs.push_back({it->amount(), i});
+ req.outputs.push_back({amount, i});
++num_found;
}
}
@@ -2457,6 +2758,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
[](const COMMAND_RPC_GET_OUTPUTS::out &a, const COMMAND_RPC_GET_OUTPUTS::out &b) { return a.index < b.index; });
}
+ for (auto i: req.outputs)
+ LOG_PRINT_L1("asking for output " << i.index << " for " << print_money(i.amount));
+
// get the keys for those
m_daemon_rpc_mutex.lock();
r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_outs.bin", req, daemon_resp, m_http_client, 200000);
@@ -2475,9 +2779,10 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
{
outs.push_back(std::vector<entry>());
outs.back().reserve(fake_outputs_count + 1);
+ const rct::key mask = it->is_rct() ? rct::commit(it->amount(), it->m_mask) : rct::zeroCommit(it->amount());
// pick real out first (it will be sorted when done)
- outs.back().push_back({it->m_global_output_index, boost::get<txout_to_key>(it->m_tx.vout[it->m_internal_output_index].target).key});
+ outs.back().push_back(std::make_tuple(it->m_global_output_index, boost::get<txout_to_key>(it->m_tx.vout[it->m_internal_output_index].target).key, mask));
// then pick others in random order till we reach the required number
// since we use an equiprobable pick here, we don't upset the triangular distribution
@@ -2496,7 +2801,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
continue;
if (o > 0 && daemon_resp.outs[i].key == daemon_resp.outs[i - 1].key) // don't add duplicates
continue;
- outs.back().push_back({req.outputs[i].index, daemon_resp.outs[i].key});
+ outs.back().push_back(std::make_tuple(req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask));
}
if (outs.back().size() < fake_outputs_count + 1)
{
@@ -2505,14 +2810,14 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
else
{
// sort the subsection, so any spares are reset in order
- std::sort(outs.back().begin(), outs.back().end(), [](const entry &a, const entry &b) { return a.first < b.first; });
+ std::sort(outs.back().begin(), outs.back().end(), [](const entry &a, const entry &b) { return std::get<0>(a) < std::get<0>(b); });
// sanity check
for (size_t n = 1; n < outs.back().size(); ++n)
{
- THROW_WALLET_EXCEPTION_IF(outs.back()[n].first == outs.back()[n-1].first, error::wallet_internal_error,
+ THROW_WALLET_EXCEPTION_IF(std::get<0>(outs.back()[n]) == std::get<0>(outs.back()[n-1]), error::wallet_internal_error,
"Duplicate indices though we did not ask for any");
- THROW_WALLET_EXCEPTION_IF(outs.back()[n].second == outs.back()[n-1].second, error::wallet_internal_error,
+ THROW_WALLET_EXCEPTION_IF(std::get<1>(outs.back()[n]) == std::get<1>(outs.back()[n-1]), error::wallet_internal_error,
"Duplicate keys after we have weeded them out");
}
}
@@ -2525,10 +2830,47 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
for (transfer_container::iterator it: selected_transfers)
{
std::vector<entry> v;
- v.push_back({it->m_global_output_index, boost::get<txout_to_key>(it->m_tx.vout[it->m_internal_output_index].target).key});
+ const rct::key mask = it->is_rct() ? rct::commit(it->amount(), it->m_mask) : rct::zeroCommit(it->amount());
+ v.push_back(std::make_tuple(it->m_global_output_index, boost::get<txout_to_key>(it->m_tx.vout[it->m_internal_output_index].target).key, mask));
outs.push_back(v);
}
}
+}
+
+template<typename T>
+void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
+{
+ using namespace cryptonote;
+ // throw if attempting a transaction with no destinations
+ THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
+
+ uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
+ uint64_t needed_money = fee;
+ LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
+
+ // calculate total amount being sent to all destinations
+ // throw if total amount overflows uint64_t
+ BOOST_FOREACH(auto& dt, dsts)
+ {
+ THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
+ needed_money += dt.amount;
+ LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
+ THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet);
+ }
+
+ uint64_t found_money = 0;
+ BOOST_FOREACH(auto it, selected_transfers)
+ {
+ found_money += it->amount();
+ }
+
+ LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee));
+ THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
+
+ typedef std::tuple<uint64_t, crypto::public_key, rct::key> entry;
+ std::vector<std::vector<entry>> outs;
+ get_outs(outs, selected_transfers, fake_outputs_count); // may throw
//prepare inputs
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
@@ -2540,13 +2882,16 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
cryptonote::tx_source_entry& src = sources.back();
transfer_details& td = *it;
src.amount = td.amount();
+ src.rct = td.is_rct();
//paste keys (fake and real)
for (size_t n = 0; n < fake_outputs_count + 1; ++n)
{
tx_output_entry oe;
- oe.first = outs[out_index][n].first;
- oe.second = outs[out_index][n].second;
+ oe.first = std::get<0>(outs[out_index][n]);
+ oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
+ oe.second.mask = std::get<2>(outs[out_index][n]);
+
src.outputs.push_back(oe);
++i;
}
@@ -2561,7 +2906,8 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
- real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
+ real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
+ real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
*it_to_replace = real_oe;
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
src.real_output = it_to_replace - src.outputs.begin();
@@ -2621,6 +2967,225 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
ptx.dests = dsts;
}
+void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx)
+{
+ using namespace cryptonote;
+ // throw if attempting a transaction with no destinations
+ THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
+
+ uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
+ uint64_t needed_money = fee;
+ LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
+
+ // calculate total amount being sent to all destinations
+ // throw if total amount overflows uint64_t
+ BOOST_FOREACH(auto& dt, dsts)
+ {
+ THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
+ needed_money += dt.amount;
+ LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
+ THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet);
+ }
+
+ uint64_t found_money = 0;
+ BOOST_FOREACH(auto it, selected_transfers)
+ {
+ found_money += it->amount();
+ }
+
+ LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee));
+ THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
+
+ typedef std::tuple<uint64_t, crypto::public_key, rct::key> entry;
+ std::vector<std::vector<entry>> outs;
+ get_outs(outs, selected_transfers, fake_outputs_count); // may throw
+
+ //prepare inputs
+ size_t i = 0, out_index = 0;
+ std::vector<cryptonote::tx_source_entry> sources;
+ BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
+ {
+ sources.resize(sources.size()+1);
+ cryptonote::tx_source_entry& src = sources.back();
+ transfer_details& td = *it;
+ src.amount = td.amount();
+ src.rct = td.is_rct();
+ //paste mixin transaction
+
+ typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
+ for (size_t n = 0; n < fake_outputs_count + 1; ++n)
+ {
+ tx_output_entry oe;
+ oe.first = std::get<0>(outs[out_index][n]);
+ oe.second.dest = rct::pk2rct(std::get<1>(outs[out_index][n]));
+ oe.second.mask = std::get<2>(outs[out_index][n]);
+ src.outputs.push_back(oe);
+ }
+ ++i;
+
+ //paste real transaction to the random index
+ auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
+ {
+ return a.first == td.m_global_output_index;
+ });
+ THROW_WALLET_EXCEPTION_IF(it_to_replace == src.outputs.end(), error::wallet_internal_error,
+ "real output not found");
+
+ tx_output_entry real_oe;
+ real_oe.first = td.m_global_output_index;
+ real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
+ real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
+ *it_to_replace = real_oe;
+ src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
+ src.real_output = it_to_replace - src.outputs.begin();
+ src.real_output_in_tx_index = td.m_internal_output_index;
+ src.mask = td.m_mask;
+ detail::print_source_entry(src);
+ ++out_index;
+ }
+
+ cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
+ if (needed_money < found_money)
+ {
+ change_dts.addr = m_account.get_keys().m_account_address;
+ change_dts.amount = found_money - needed_money;
+ dsts.push_back(change_dts);
+ }
+
+ crypto::secret_key tx_key;
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, dsts, extra, tx, unlock_time, tx_key, true);
+ THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet);
+ THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
+
+ std::string key_images;
+ bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
+ key_images += boost::to_string(in.k_image) + " ";
+ return true;
+ });
+ THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
+
+ ptx.key_images = key_images;
+ ptx.fee = fee;
+ ptx.dust = 0;
+ ptx.dust_added_to_fee = false;
+ ptx.tx = tx;
+ ptx.change_dts = change_dts;
+ ptx.selected_transfers = selected_transfers;
+ ptx.tx_key = tx_key;
+ ptx.dests = dsts;
+}
+
+static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
+{
+ size_t size = 0;
+
+ // tx prefix
+
+ // first few bytes
+ size += 1 + 6;
+
+ // vin
+ size += n_inputs * (1+6+(mixin+1)*2+32);
+
+ // vout
+ size += n_outputs * (6+32);
+
+ // extra
+ size += 40;
+
+ // rct signatures
+
+ // simple
+ size += 1;
+
+ // message
+ size += 32;
+
+ // rangeSigs
+ size += (2*64*32+32+64*32) * n_outputs;
+
+ // MGs - only the last slot of II is saved, the rest can be reconstructed
+ size += n_inputs * (32 * (mixin+1) * n_inputs + 32 + 32 * (/*n_inputs+*/1));
+
+ // mixRing - not serialized, can be reconstructed
+ /* size += 2 * 32 * (mixin+1) * n_inputs; */
+
+ // pseudoOuts
+ size += 32 * n_outputs;
+ // ecdhInfo
+ size += 3 * 32 * n_outputs;
+ // outPk - only commitment is saved
+ size += 1 * 32 * n_outputs;
+ // txnFee
+ size += 4;
+
+ LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
+ return size;
+}
+
+std::vector<size_t> wallet2::pick_prefered_rct_inputs(uint64_t needed_money) const
+{
+ std::vector<size_t> picks;
+ float current_output_relatdness = 1.0f;
+
+ LOG_PRINT_L2("pick_prefered_rct_inputs: needed_money " << print_money(needed_money));
+
+ // try to find a rct input of enough size
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td))
+ {
+ LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
+ picks.push_back(i);
+ return picks;
+ }
+ }
+
+ // then try to find two outputs
+ // this could be made better by picking one of the outputs to be a small one, since those
+ // are less useful since often below the needed money, so if one can be used in a pair,
+ // it gets rid of it for the future
+ for (size_t i = 0; i < m_transfers.size(); ++i)
+ {
+ const transfer_details& td = m_transfers[i];
+ if (!td.m_spent && td.is_rct() && is_transfer_unlocked(td))
+ {
+ LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount()));
+ for (size_t j = i + 1; j < m_transfers.size(); ++j)
+ {
+ const transfer_details& td2 = m_transfers[j];
+ if (!td2.m_spent && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2))
+ {
+ // update our picks if those outputs are less related than any we
+ // already found. If the same, don't update, and oldest suitable outputs
+ // will be used in preference.
+ float relatedness = get_output_relatedness(td, td2);
+ LOG_PRINT_L2(" with input " << j << ", " << print_money(td2.amount()) << ", relatedness " << relatedness);
+ if (relatedness < current_output_relatdness)
+ {
+ // reset the current picks with those, and return them directly
+ // if they're unrelated. If they are related, we'll end up returning
+ // them if we find nothing better
+ picks.clear();
+ picks.push_back(i);
+ picks.push_back(j);
+ LOG_PRINT_L0("we could use " << i << " and " << j);
+ if (relatedness == 0.0f)
+ return picks;
+ current_output_relatdness = relatedness;
+ }
+ }
+ }
+ }
+ }
+
+ return picks;
+}
+
// Another implementation of transaction creation that is hopefully better
// While there is anything left to pay, it goes through random outputs and tries
// to fill the next destination/amount. If it fully fills it, it will use the
@@ -2662,12 +3227,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
bool adding_fee; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
+ const bool use_rct = use_fork_rules(4, 0);
+
+ fee_multiplier = sanitize_fee_multiplier(fee_multiplier);
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
- fee_multiplier = sanitize_fee_multiplier (fee_multiplier);
-
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
needed_money = 0;
@@ -2686,9 +3252,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && is_transfer_unlocked(td))
+ if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
- if (is_valid_decomposed_amount(td.amount()))
+ if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i);
else
unused_dust_indices.push_back(i);
@@ -2704,6 +3270,28 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
adding_fee = false;
needed_fee = 0;
+ // for rct, since we don't see the amounts, we will try to make all transactions
+ // look the same, with 1 or 2 inputs, and 2 outputs. One input is preferable, as
+ // this prevents linking to another by provenance analysis, but two is ok if we
+ // try to pick outputs not from the same block. We will get two outputs, one for
+ // the destination, and one for change.
+ std::vector<size_t> prefered_inputs;
+ uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
+ rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
+ if (use_rct && get_num_rct_outputs() >= rct_outs_needed)
+ {
+ // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
+ // will get us a known fee.
+ uint64_t estimated_fee = calculate_fee(estimate_rct_tx_size(2, fake_outs_count + 1, 2), fee_multiplier);
+ prefered_inputs = pick_prefered_rct_inputs(needed_money + estimated_fee);
+ if (!prefered_inputs.empty())
+ {
+ string s;
+ for (auto i: prefered_inputs) s += print_money(m_transfers[i].amount()) + " ";
+ LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s);
+ }
+ }
+
// while we have something to send
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee) {
TX &tx = txes.back();
@@ -2716,7 +3304,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
- size_t idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices);
+ size_t idx = !prefered_inputs.empty() ? pop_back(prefered_inputs) : !unused_transfers_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : pop_best_value(unused_dust_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@@ -2765,7 +3353,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- try_tx = dsts.empty() || (tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ size_t estimated_rct_tx_size;
+ if (use_rct)
+ estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
+ else
+ estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES;
+ try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
}
if (try_tx) {
@@ -2776,8 +3369,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs");
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ test_tx, test_ptx);
+ else
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
@@ -2814,11 +3411,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
else
{
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ test_tx, test_ptx);
+ else
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
- LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
- " fee and " << print_money(test_ptx.change_dts.amount) << " change");
+ LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
+ " fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx;
tx.ptx = test_ptx;
@@ -2862,7 +3463,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
return ptx_vector;
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
+std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -2877,16 +3478,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
-
- fee_multiplier = sanitize_fee_multiplier(fee_multiplier);
+ const bool use_rct = use_fork_rules(4, 0);
// gather all our dust and non dust outputs
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && is_transfer_unlocked(td))
+ if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
- if (is_valid_decomposed_amount(td.amount()))
+ if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i);
else
unused_dust_indices.push_back(i);
@@ -2908,7 +3508,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
// get a random unspent output and use it to pay next chunk. We try to alternate
// dust and non dust to ensure we never get with only dust, from which we might
// get a tx that can't pay for itself
- size_t idx = unused_transfers_indices.empty() ? pop_random_value(unused_dust_indices) : unused_dust_indices.empty() ? pop_random_value(unused_transfers_indices) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > FEE_PER_KB * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_random_value(unused_dust_indices) : pop_random_value(unused_transfers_indices);
+ size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > FEE_PER_KB * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@@ -2921,7 +3521,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit);
- bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || (tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES >= TX_SIZE_TARGET(upper_transaction_size_limit));
+ size_t estimated_rct_tx_size;
+ if (use_rct)
+ estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
+ else
+ estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES;
+ bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
if (try_tx) {
cryptonote::transaction test_tx;
@@ -2933,8 +3538,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs");
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ test_tx, test_ptx);
+ else
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(txBlob, fee_multiplier);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
@@ -2946,8 +3555,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
do {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
tx.dsts[0].amount = available_for_fee - needed_fee;
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
- detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
+ if (use_rct)
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ test_tx, test_ptx);
+ else
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
+ detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
@@ -3057,6 +3670,7 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
cryptonote::tx_source_entry& src = sources.back();
transfer_details& td = *it;
src.amount = td.amount();
+ src.rct = td.is_rct();
//paste real transaction to the random index
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
@@ -3065,7 +3679,8 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
});
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
- real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
+ real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
+ real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
src.real_output = interted_it - src.outputs.begin();
@@ -3113,36 +3728,45 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::use_fork_rules(uint8_t version)
+void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
{
- cryptonote::COMMAND_RPC_GET_HEIGHT::request req = AUTO_VAL_INIT(req);
- cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res);
epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
- m_daemon_rpc_mutex.unlock();
- CHECK_AND_ASSERT_MES(r, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "Failed to get current blockchain height");
-
- m_daemon_rpc_mutex.lock();
req_t.jsonrpc = "2.0";
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "hard_fork_info";
req_t.params.version = version;
- r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ CHECK_AND_ASSERT_THROW_MES(r, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_THROW_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_THROW_MES(resp_t.result.status == CORE_RPC_STATUS_OK, "Failed to get hard fork status");
+
+ earliest_height = resp_t.result.earliest_height;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::use_fork_rules(uint8_t version, uint64_t early_blocks)
+{
+ cryptonote::COMMAND_RPC_GET_HEIGHT::request req = AUTO_VAL_INIT(req);
+ cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res);
+
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
+ CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "Failed to get current blockchain height");
+
+ uint64_t earliest_height;
+ get_hard_fork_info(version, earliest_height); // can throw
- bool close_enough = res.height >= resp_t.result.earliest_height - 10; // start using the rules a bit beforehand
+ bool close_enough = res.height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand
if (close_enough)
- LOG_PRINT_L2("Using HF1 rules");
+ LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
- LOG_PRINT_L2("Not using HF1 rules");
+ LOG_PRINT_L2("Not using v" << (unsigned)version << " rules");
return close_enough;
}
//----------------------------------------------------------------------------------------------------
@@ -3150,7 +3774,7 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
{
if (m_upper_transaction_size_limit > 0)
return m_upper_transaction_size_limit;
- uint64_t full_reward_zone = use_fork_rules(2) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
+ uint64_t full_reward_zone = use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
@@ -3213,6 +3837,8 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
}
return select_available_outputs([mixable, atleast](const transfer_details &td) {
+ if (td.is_rct())
+ return false;
const uint64_t amount = td.amount();
if (atleast) {
if (mixable.find(amount) != mixable.end())
@@ -3226,6 +3852,28 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
});
}
//----------------------------------------------------------------------------------------------------
+uint64_t wallet2::get_num_rct_outputs()
+{
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+ m_daemon_rpc_mutex.lock();
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "get_output_histogram";
+ req_t.params.amounts.push_back(0);
+ req_t.params.min_count = 0;
+ req_t.params.max_count = 0;
+ bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status);
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
+
+ return resp_t.result.histogram[0].instances;
+}
+//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon)
{
// request all outputs with less than 3 instances
@@ -3241,7 +3889,7 @@ std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemo
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon)
{
// From hard fork 1, we don't consider small amounts to be dust anymore
- const bool hf1_rules = use_fork_rules(2); // first hard fork has version 2
+ const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
// may throw
@@ -3290,7 +3938,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
// mark transfers to be used as "spent"
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
- it->m_spent = true;
+ {
+ set_spent(*it, 0);
+ }
}
// if we made it this far, we've selected our transactions. committing them will mark them spent,
@@ -3300,8 +3950,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
-
+ {
+ set_unspent(*it2);
+ }
}
// if we made it this far, we're OK to actually send the transactions
@@ -3317,8 +3968,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
-
+ {
+ set_unspent(*it2);
+ }
}
if (attempt_count >= MAX_SPLIT_ATTEMPTS)
@@ -3335,8 +3987,9 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
{
// mark transfers to be used as not spent
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
- it2->m_spent = false;
-
+ {
+ set_unspent(*it2);
+ }
}
throw;
@@ -3521,7 +4174,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
{
transfer_details &td = m_transfers[n];
- uint64_t amount = td.m_tx.vout[td.m_internal_output_index].amount;
+ uint64_t amount = td.amount();
td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
if (td.m_spent)
spent += amount;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index d0c514a6d..0e87614b4 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -31,6 +31,7 @@
#pragma once
#include <memory>
+#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <atomic>
@@ -46,6 +47,8 @@
#include "common/unordered_containers_boost_serialization.h"
#include "crypto/chacha8.h"
#include "crypto/hash.h"
+#include "ringct/rctTypes.h"
+#include "ringct/rctOps.h"
#include "wallet_errors.h"
@@ -58,8 +61,8 @@ namespace tools
{
public:
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) {}
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) {}
+ virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {}
+ virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {}
virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {}
virtual ~i_wallet2_callback() {}
};
@@ -96,13 +99,19 @@ namespace tools
struct transfer_details
{
uint64_t m_block_height;
- cryptonote::transaction m_tx;
+ cryptonote::transaction_prefix m_tx;
+ crypto::hash m_txid;
size_t m_internal_output_index;
uint64_t m_global_output_index;
bool m_spent;
+ uint64_t m_spent_height;
crypto::key_image m_key_image; //TODO: key_image stored twice :(
+ rct::key m_mask;
+ uint64_t m_amount;
+ bool m_rct;
- uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; }
+ bool is_rct() const { return m_rct; }
+ uint64_t amount() const { return m_amount; }
};
struct payment_details
@@ -116,7 +125,9 @@ namespace tools
struct unconfirmed_transfer_details
{
- cryptonote::transaction m_tx;
+ cryptonote::transaction_prefix m_tx;
+ uint64_t m_amount_in;
+ uint64_t m_amount_out;
uint64_t m_change;
time_t m_sent_time;
std::vector<cryptonote::tx_destination_entry> m_dests;
@@ -137,7 +148,7 @@ namespace tools
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash) {}
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
- m_amount_out(get_outs_money_amount(utd.m_tx)), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp) { get_inputs_money_amount(utd.m_tx, m_amount_in); }
+ m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp) {}
};
typedef std::vector<transfer_details> transfer_container;
@@ -289,6 +300,8 @@ namespace tools
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
+ void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
@@ -308,6 +321,7 @@ namespace tools
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
void rescan_spent();
void rescan_blockchain(bool refresh = true);
+ bool is_transfer_unlocked(const transfer_details& td) const;
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
@@ -339,6 +353,8 @@ namespace tools
if(ver < 13)
return;
a & m_unconfirmed_payments;
+ if(ver < 14)
+ return;
}
/*!
@@ -376,8 +392,10 @@ namespace tools
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
+ uint64_t get_num_rct_outputs();
- bool use_fork_rules(uint8_t version);
+ void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
+ bool use_fork_rules(uint8_t version, uint64_t early_blocks = 0);
std::string get_wallet_file() const;
std::string get_keys_file() const;
@@ -388,6 +406,9 @@ namespace tools
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon);
+ size_t pop_best_value_from(const transfer_container &transfers, std::vector<size_t> &unused_dust_indices, const std::list<transfer_container::iterator>& selected_transfers) const;
+ size_t pop_best_value(std::vector<size_t> &unused_dust_indices, const std::list<transfer_container::iterator>& selected_transfers) const;
+
void set_tx_note(const crypto::hash &txid, const std::string &note);
std::string get_tx_note(const crypto::hash &txid) const;
@@ -413,32 +434,37 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const std::string& password);
- void process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
- void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height);
+ void process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
+ void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices);
void detach_blockchain(uint64_t height);
void get_short_chain_history(std::list<crypto::hash>& ids) const;
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const;
- bool is_transfer_unlocked(const transfer_details& td) const;
bool clear();
- void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks);
+ void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history);
- void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, bool &error);
- void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added);
+ void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, bool &error);
+ void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added);
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<transfer_container::iterator>& selected_transfers, bool trusted_daemon);
bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height);
void process_outgoing(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received);
- void add_unconfirmed_tx(const cryptonote::transaction& tx, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
+ void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
void generate_genesis(cryptonote::block& b);
void check_genesis(const crypto::hash& genesis_hash) const; //throws
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
crypto::hash get_payment_id(const pending_tx &ptx) const;
- void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, uint64_t &money_transfered, bool &error) const;
+ void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_tranaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
uint64_t sanitize_fee_multiplier(uint64_t fee_multiplier) const;
+ float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
+ std::vector<size_t> pick_prefered_rct_inputs(uint64_t needed_money) const;
+ void set_spent(transfer_details &td, uint64_t height);
+ void set_unspent(transfer_details &td);
+ template<typename entry>
+ void get_outs(std::vector<std::vector<entry>> &outs, const std::list<transfer_container::iterator> &selected_transfers, size_t fake_outputs_count);
cryptonote::account_base m_account;
std::string m_daemon_address;
@@ -478,9 +504,10 @@ namespace tools
uint64_t m_refresh_from_block_height;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 13)
+BOOST_CLASS_VERSION(tools::wallet2, 14)
+BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 4)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
-BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 3)
+BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
namespace boost
@@ -488,14 +515,72 @@ namespace boost
namespace serialization
{
template <class Archive>
+ inline void initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
+ {
+ }
+ template<>
+ inline void initialize_transfer_details(boost::archive::binary_iarchive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
+ {
+ if (ver < 1)
+ {
+ x.m_mask = rct::identity();
+ x.m_amount = x.m_tx.vout[x.m_internal_output_index].amount;
+ }
+ if (ver < 2)
+ {
+ x.m_spent_height = 0;
+ }
+ if (ver < 4)
+ {
+ x.m_rct = x.m_tx.vout[x.m_internal_output_index].amount == 0;
+ }
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
{
a & x.m_block_height;
a & x.m_global_output_index;
a & x.m_internal_output_index;
- a & x.m_tx;
+ if (ver < 3)
+ {
+ cryptonote::transaction tx;
+ a & tx;
+ x.m_tx = (const cryptonote::transaction_prefix&)tx;
+ x.m_txid = cryptonote::get_transaction_hash(tx);
+ }
+ else
+ {
+ a & x.m_tx;
+ }
a & x.m_spent;
a & x.m_key_image;
+ if (ver < 1)
+ {
+ // ensure mask and amount are set
+ initialize_transfer_details(a, x, ver);
+ return;
+ }
+ a & x.m_mask;
+ a & x.m_amount;
+ if (ver < 2)
+ {
+ initialize_transfer_details(a, x, ver);
+ return;
+ }
+ a & x.m_spent_height;
+ if (ver < 3)
+ {
+ initialize_transfer_details(a, x, ver);
+ return;
+ }
+ a & x.m_txid;
+ if (ver < 4)
+ {
+ initialize_transfer_details(a, x, ver);
+ return;
+ }
+ a & x.m_rct;
}
template <class Archive>
@@ -503,7 +588,16 @@ namespace boost
{
a & x.m_change;
a & x.m_sent_time;
- a & x.m_tx;
+ if (ver < 5)
+ {
+ cryptonote::transaction tx;
+ a & tx;
+ x.m_tx = (const cryptonote::transaction_prefix&)tx;
+ }
+ else
+ {
+ a & x.m_tx;
+ }
if (ver < 1)
return;
a & x.m_dests;
@@ -514,6 +608,10 @@ namespace boost
if (ver < 3)
return;
a & x.m_timestamp;
+ if (ver < 4)
+ return;
+ a & x.m_amount_in;
+ a & x.m_amount_out;
}
template <class Archive>
@@ -688,6 +786,7 @@ namespace tools
cryptonote::tx_source_entry& src = sources.back();
transfer_details& td = *it;
src.amount = td.amount();
+ src.rct = false;
//paste mixin transaction
if(daemon_resp.outs.size())
{
@@ -698,7 +797,8 @@ namespace tools
continue;
tx_output_entry oe;
oe.first = daemon_oe.global_amount_index;
- oe.second = daemon_oe.out_key;
+ oe.second.dest = rct::pk2rct(daemon_oe.out_key);
+ oe.second.mask = rct::identity();
src.outputs.push_back(oe);
if(src.outputs.size() >= fake_outputs_count)
break;
@@ -713,7 +813,8 @@ namespace tools
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
- real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
+ real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
+ real_oe.second.mask = rct::identity();
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
src.real_output = interted_it - src.outputs.begin();
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index debd7056a..4c190b005 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -235,11 +235,11 @@ namespace tools
try
{
uint64_t mixin = req.mixin;
- if (mixin < 2 && m_wallet.use_fork_rules(2)) {
+ if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) {
LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
mixin = 2;
}
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions(dsts, mixin, req.unlock_time, req.fee_multiplier, extra, req.trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.fee_multiplier, extra, req.trusted_daemon);
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
@@ -254,7 +254,9 @@ namespace tools
// populate response with tx hash
res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
if (req.get_tx_key)
+ {
res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
+ }
return true;
}
catch (const tools::error::daemon_busy& e)
@@ -300,15 +302,12 @@ namespace tools
try
{
uint64_t mixin = req.mixin;
- if (mixin < 2 && m_wallet.use_fork_rules(2)) {
+ if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) {
LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
mixin = 2;
}
std::vector<wallet2::pending_tx> ptx_vector;
- if (req.new_algorithm)
- ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.fee_multiplier, extra, req.trusted_daemon);
- else
- ptx_vector = m_wallet.create_transactions(dsts, mixin, req.unlock_time, req.fee_multiplier, extra, req.trusted_daemon);
+ ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.fee_multiplier, extra, req.trusted_daemon);
m_wallet.commit_tx(ptx_vector);
@@ -317,7 +316,9 @@ namespace tools
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
+ {
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
+ }
}
return true;
@@ -363,7 +364,9 @@ namespace tools
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
+ {
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
+ }
}
return true;
@@ -422,7 +425,9 @@ namespace tools
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
if (req.get_tx_keys)
+ {
res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
+ }
}
return true;
@@ -693,7 +698,7 @@ namespace tools
rpc_transfers.amount = td.amount();
rpc_transfers.spent = td.m_spent;
rpc_transfers.global_index = td.m_global_output_index;
- rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(td.m_tx));
+ rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
res.transfers.push_back(rpc_transfers);
}
@@ -961,10 +966,8 @@ namespace tools
entry.payment_id = entry.payment_id.substr(0,16);
entry.height = 0;
entry.timestamp = pd.m_timestamp;
- uint64_t amount = 0;
- cryptonote::get_inputs_money_amount(pd.m_tx, amount);
- entry.fee = amount - get_outs_money_amount(pd.m_tx);
- entry.amount = amount - pd.m_change - entry.fee;
+ entry.fee = pd.m_amount_in - pd.m_amount_out;
+ entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
entry.note = m_wallet.get_tx_note(i->first);
}
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index d7f01d9ee..27b897dab 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -132,10 +132,12 @@ namespace wallet_rpc
{
std::string tx_hash;
std::string tx_key;
+ std::list<std::string> amount_keys;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(amount_keys)
END_KV_SERIALIZE_MAP()
};
};
@@ -149,7 +151,6 @@ namespace wallet_rpc
uint64_t mixin;
uint64_t unlock_time;
std::string payment_id;
- bool new_algorithm;
bool get_tx_keys;
bool trusted_daemon;
@@ -159,12 +160,20 @@ namespace wallet_rpc
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
- KV_SERIALIZE(new_algorithm)
KV_SERIALIZE(get_tx_keys)
KV_SERIALIZE(trusted_daemon)
END_KV_SERIALIZE_MAP()
};
+ struct key_list
+ {
+ std::list<std::string> keys;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(keys)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct response
{
std::list<std::string> tx_hash_list;
@@ -190,6 +199,15 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
+ struct key_list
+ {
+ std::list<std::string> keys;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(keys)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct response
{
std::list<std::string> tx_hash_list;
@@ -225,6 +243,15 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
+ struct key_list
+ {
+ std::list<std::string> keys;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(keys)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct response
{
std::list<std::string> tx_hash_list;
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h
index 0315fc8c8..0f6d6571e 100644
--- a/tests/core_proxy/core_proxy.h
+++ b/tests/core_proxy/core_proxy.h
@@ -34,7 +34,6 @@
#include "cryptonote_core/cryptonote_basic_impl.h"
#include "cryptonote_core/verification_context.h"
-#include "cryptonote_core/blockchain_storage.h"
#include <unordered_map>
namespace tests
@@ -82,7 +81,7 @@ namespace tests
bool on_idle(){return true;}
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;}
bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;}
- cryptonote::blockchain_storage &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); }
+ cryptonote::Blockchain &get_blockchain_storage() { throw std::runtime_error("Called invalid member function: please never call get_blockchain_storage on the TESTING class proxy_core."); }
bool get_test_drop_download() {return true;}
bool get_test_drop_download_height() {return true;}
bool prepare_handle_incoming_blocks(const std::list<cryptonote::block_complete_entry> &blocks) { return true; }
diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt
index c34039c17..6f07fbd25 100644
--- a/tests/core_tests/CMakeLists.txt
+++ b/tests/core_tests/CMakeLists.txt
@@ -39,7 +39,8 @@ set(core_tests_sources
ring_signature_1.cpp
transaction_tests.cpp
tx_validation.cpp
- v2_tests.cpp)
+ v2_tests.cpp
+ rct.cpp)
set(core_tests_headers
block_reward.h
@@ -54,7 +55,8 @@ set(core_tests_headers
ring_signature_1.h
transaction_tests.h
tx_validation.h
- v2_tests.h)
+ v2_tests.h
+ rct.h)
add_executable(coretests
${core_tests_sources}
diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp
index bce0980bf..df8972556 100644
--- a/tests/core_tests/block_validation.cpp
+++ b/tests/core_tests/block_validation.cpp
@@ -335,8 +335,9 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events
tx_source_entry se;
se.amount = blk_0.miner_tx.vout[0].amount;
- se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key));
+ se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
se.real_output = 0;
+ se.rct = false;
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
se.real_output_in_tx_index = 0;
std::vector<tx_source_entry> sources;
@@ -377,8 +378,9 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
tx_source_entry se;
se.amount = blk_1.miner_tx.vout[0].amount;
- se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key));
+ se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
se.real_output = 0;
+ se.rct = false;
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
se.real_output_in_tx_index = 0;
std::vector<tx_source_entry> sources;
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index efd0bf1ea..4cb70e745 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -94,11 +94,11 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl
return get_already_generated_coins(blk_hash);
}
-void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins)
+void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, uint8_t hf_version)
{
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
uint64_t block_reward;
- get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, 1);
+ get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version);
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size);
}
@@ -215,7 +215,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/,
const transaction& miner_tx/* = transaction()*/,
const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/,
- size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/)
+ size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/)
{
blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION;
@@ -223,6 +223,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block);
blk.tx_hashes = actual_params & bf_tx_hashes ? tx_hashes : std::vector<crypto::hash>();
max_outs = actual_params & bf_max_outs ? max_outs : 9999;
+ hf_version = actual_params & bf_hf_version ? hf_version : 1;
size_t height = get_block_height(prev_block) + 1;
uint64_t already_generated_coins = get_already_generated_coins(prev_block);
@@ -236,7 +237,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
{
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx);
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
- if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs))
+ if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version))
return false;
}
@@ -245,7 +246,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty();
fill_nonce(blk, a_diffic, height);
- add_block(blk, txs_sizes, block_sizes, already_generated_coins);
+ add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version);
return true;
}
@@ -412,7 +413,7 @@ bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_o
if (append)
{
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
- output_entries.push_back(tx_source_entry::output_entry(oi.idx, otk.key));
+ output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()})));
}
}
@@ -457,6 +458,7 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
continue;
ts.real_output = realOutput;
+ ts.rct = false;
sources.push_back(ts);
@@ -544,7 +546,7 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
out.target = txout_to_key(out_eph_public_key);
tx.vout.push_back(out);
- tx.version = CURRENT_TRANSACTION_VERSION;
+ tx.version = 1;
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
return true;
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index 0e5dbb0e4..047d2c81c 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -221,7 +221,8 @@ public:
bf_miner_tx = 1 << 4,
bf_tx_hashes = 1 << 5,
bf_diffic = 1 << 6,
- bf_max_outs = 1 << 7
+ bf_max_outs = 1 << 7,
+ bf_hf_version= 1 << 8
};
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
@@ -229,7 +230,8 @@ public:
uint64_t get_already_generated_coins(const crypto::hash& blk_id) const;
uint64_t get_already_generated_coins(const cryptonote::block& blk) const;
- void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins);
+ void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins,
+ uint8_t hf_version = 1);
bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list);
@@ -241,7 +243,8 @@ public:
const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0,
uint8_t minor_ver = 0, uint64_t timestamp = 0, const crypto::hash& prev_id = crypto::hash(),
const cryptonote::difficulty_type& diffic = 1, const cryptonote::transaction& miner_tx = cryptonote::transaction(),
- const std::vector<crypto::hash>& tx_hashes = std::vector<crypto::hash>(), size_t txs_sizes = 0, size_t max_outs = 999);
+ const std::vector<crypto::hash>& tx_hashes = std::vector<crypto::hash>(), size_t txs_sizes = 0, size_t max_outs = 999,
+ uint8_t hf_version = 1);
bool construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
const cryptonote::account_base& miner_acc, const std::vector<crypto::hash>& tx_hashes, size_t txs_size);
@@ -472,11 +475,11 @@ inline bool replay_events_through_core(cryptonote::core& cr, const std::vector<t
//--------------------------------------------------------------------------
template<typename t_test_class>
struct get_test_options {
- const std::pair<uint8_t, uint64_t> hard_forks[1];
+ const std::pair<uint8_t, uint64_t> hard_forks[2];
const cryptonote::test_options test_options = {
hard_forks
};
- get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0)}{}
+ get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{}
};
//--------------------------------------------------------------------------
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index e20f7a152..09cdb9227 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -173,6 +173,35 @@ int main(int argc, char* argv[])
// GENERATE_AND_PLAY(gen_v2_tx_unmixable_two);
GENERATE_AND_PLAY(gen_v2_tx_dust);
+ GENERATE_AND_PLAY(gen_rct_tx_valid_from_pre_rct);
+ GENERATE_AND_PLAY(gen_rct_tx_valid_from_rct);
+ GENERATE_AND_PLAY(gen_rct_tx_valid_from_mixed);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_real_dest);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_real_mask);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_fake_dest);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_bad_fake_mask);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_bad_real_dest);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_bad_real_mask);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_bad_fake_dest);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_bad_fake_mask);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_spend_with_zero_commit);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_zero_vin_amount);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_non_zero_vin_amount);
+ GENERATE_AND_PLAY(gen_rct_tx_non_zero_vout_amount);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_duplicate_key_image);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_duplicate_key_image);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_wrong_key_image);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_wrong_key_image);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_wrong_fee);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_wrong_fee);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_remove_vin);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_remove_vin);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_add_vout);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_add_vout);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee);
+ GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra);
+ GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra);
+
std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta);
std::cout << "\nREPORT:\n";
std::cout << " Test run: " << tests_count << '\n';
diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h
index 4da87a973..1b9ebd756 100644
--- a/tests/core_tests/chaingen_tests_list.h
+++ b/tests/core_tests/chaingen_tests_list.h
@@ -40,6 +40,7 @@
#include "ring_signature_1.h"
#include "tx_validation.h"
#include "v2_tests.h"
+#include "rct.h"
/************************************************************************/
/* */
/************************************************************************/
diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl
index 55e5f4ec9..f97d48851 100644
--- a/tests/core_tests/double_spend.inl
+++ b/tests/core_tests/double_spend.inl
@@ -128,8 +128,9 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even
std::vector<cryptonote::tx_source_entry> sources;
cryptonote::tx_source_entry se;
se.amount = tx_0.vout[0].amount;
- se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key));
+ se.push_output(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key, se.amount);
se.real_output = 0;
+ se.rct = false;
se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0);
se.real_output_in_tx_index = 0;
sources.push_back(se);
diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp
index 55a55b7d8..936f29675 100644
--- a/tests/core_tests/integer_overflow.cpp
+++ b/tests/core_tests/integer_overflow.cpp
@@ -61,8 +61,9 @@ namespace
{
cryptonote::tx_source_entry se;
se.amount = tx.vout[out_idx].amount;
- se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key));
+ se.push_output(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key, se.amount);
se.real_output = 0;
+ se.rct = false;
se.real_out_tx_key = get_tx_pub_key_from_extra(tx);
se.real_output_in_tx_index = out_idx;
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
new file mode 100644
index 000000000..c29854888
--- /dev/null
+++ b/tests/core_tests/rct.cpp
@@ -0,0 +1,499 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "ringct/rctSigs.h"
+#include "chaingen.h"
+#include "chaingen_tests_list.h"
+
+using namespace epee;
+using namespace crypto;
+using namespace cryptonote;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Tests
+
+bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
+ const int *out_idx, int mixin, uint64_t amount_paid, bool valid,
+ const std::function<void(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(transaction &tx)> &post_tx) const
+{
+ uint64_t ts_start = 1338224400;
+
+ GENERATE_ACCOUNT(miner_account);
+ MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
+
+ // create 4 miner accounts, and have them mine the next 4 blocks
+ cryptonote::account_base miner_accounts[4];
+ const cryptonote::block *prev_block = &blk_0;
+ cryptonote::block blocks[4];
+ for (size_t n = 0; n < 4; ++n) {
+ miner_accounts[n].generate();
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
+ 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
+ false, "Failed to generate block");
+ events.push_back(blocks[n]);
+ prev_block = blocks + n;
+ LOG_PRINT_L0("Initial miner tx " << n << ": " << obj_to_json_str(blocks[n].miner_tx));
+ }
+
+ // rewind
+ cryptonote::block blk_r, blk_last;
+ {
+ blk_last = blocks[3];
+ for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
+ {
+ cryptonote::block blk;
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
+ 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
+ false, "Failed to generate block");
+ events.push_back(blk);
+ blk_last = blk;
+ }
+ blk_r = blk_last;
+ }
+
+ // create 4 txes from these miners in another block, to generate some rct outputs
+ transaction rct_txes[4];
+ rct::key rct_tx_masks[16];
+ cryptonote::block blk_txes[4];
+ for (size_t n = 0; n < 4; ++n)
+ {
+ std::vector<crypto::hash> starting_rct_tx_hashes;
+ std::vector<tx_source_entry> sources;
+
+ sources.resize(1);
+ tx_source_entry& src = sources.back();
+
+ const size_t index_in_tx = 5;
+ src.amount = 30000000000000;
+ for (int m = 0; m < 4; ++m) {
+ src.push_output(m, boost::get<txout_to_key>(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount);
+ }
+ src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[n].miner_tx);
+ src.real_output = n;
+ src.real_output_in_tx_index = index_in_tx;
+ src.mask = rct::identity();
+ src.rct = false;
+
+ //fill outputs entry
+ tx_destination_entry td;
+ td.addr = miner_accounts[n].get_keys().m_account_address;
+ td.amount = 7390000000000;
+ std::vector<tx_destination_entry> destinations;
+ destinations.push_back(td);
+ destinations.push_back(td);
+ destinations.push_back(td);
+ destinations.push_back(td); // 30 -> 7.39 * 4
+
+ crypto::secret_key tx_key;
+ bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), sources, destinations, std::vector<uint8_t>(), rct_txes[n], 0, tx_key, true);
+ CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
+ events.push_back(rct_txes[n]);
+ starting_rct_tx_hashes.push_back(get_transaction_hash(rct_txes[n]));
+
+ for (size_t o = 0; o < 4; ++o)
+ {
+ crypto::key_derivation derivation;
+ bool r = crypto::generate_key_derivation(destinations[o].addr.m_view_public_key, tx_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
+ crypto::secret_key amount_key;
+ crypto::derivation_to_scalar(derivation, o, amount_key);
+ if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple)
+ rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
+ else
+ rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
+ }
+
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs,
+ 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 4),
+ false, "Failed to generate block");
+ events.push_back(blk_txes[n]);
+ blk_last = blk_txes[n];
+ }
+
+ // rewind
+ {
+ for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
+ {
+ cryptonote::block blk;
+ CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
+ test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
+ 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
+ crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, 4),
+ false, "Failed to generate block");
+ events.push_back(blk);
+ blk_last = blk;
+ }
+ blk_r = blk_last;
+ }
+
+ // create a tx from the requested ouputs
+ std::vector<tx_source_entry> sources;
+ size_t global_rct_idx = 6; // skip first coinbase (6 outputs)
+ size_t rct_idx = 0;
+ size_t pre_rct_idx = 0;
+ for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) {
+ sources.resize(sources.size()+1);
+ tx_source_entry& src = sources.back();
+
+ src.real_output = 0;
+ if (out_idx[out_idx_idx]) {
+ // rct
+ src.amount = 7390000000000;
+ src.real_out_tx_key = get_tx_pub_key_from_extra(rct_txes[rct_idx/4]);
+ src.real_output_in_tx_index = rct_idx&3;
+ src.mask = rct_tx_masks[rct_idx];
+ src.rct = true;
+ for (int m = 0; m <= mixin; ++m) {
+ rct::ctkey ctkey;
+ ctkey.dest = rct::pk2rct(boost::get<txout_to_key>(rct_txes[rct_idx/4].vout[rct_idx&3].target).key);
+ ctkey.mask = rct_txes[rct_idx/4].rct_signatures.outPk[rct_idx&3].mask;
+ src.outputs.push_back(std::make_pair(global_rct_idx, ctkey));
+ ++rct_idx;
+ ++global_rct_idx;
+ if (global_rct_idx % 10 == 0)
+ global_rct_idx += 6; // skip the coinbase
+ }
+ }
+ else
+ {
+ // pre rct
+ src.amount = 5000000000000;
+ src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[pre_rct_idx].miner_tx);
+ src.real_output_in_tx_index = 4;
+ src.mask = rct::identity();
+ src.rct = false;
+ for (int m = 0; m <= mixin; ++m) {
+ src.push_output(m, boost::get<txout_to_key>(blocks[pre_rct_idx].miner_tx.vout[4].target).key, src.amount);
+ ++pre_rct_idx;
+ }
+ }
+ }
+
+ //fill outputs entry
+ tx_destination_entry td;
+ td.addr = miner_account.get_keys().m_account_address;
+ td.amount = amount_paid;
+ std::vector<tx_destination_entry> destinations;
+ destinations.push_back(td);
+
+ if (pre_tx)
+ pre_tx(sources, destinations);
+
+ transaction tx;
+ crypto::secret_key tx_key;
+ bool r = construct_tx_and_get_tx_key(miner_accounts[0].get_keys(), sources, destinations, std::vector<uint8_t>(), tx, 0, tx_key, true);
+ CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
+
+ if (post_tx)
+ post_tx(tx);
+
+ if (!valid)
+ DO_CALLBACK(events, "mark_invalid_tx");
+ events.push_back(tx);
+ LOG_PRINT_L0("Test tx: " << obj_to_json_str(tx));
+
+ return true;
+}
+
+bool gen_rct_tx_valid_from_pre_rct::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL);
+}
+
+bool gen_rct_tx_valid_from_rct::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL);
+}
+
+bool gen_rct_tx_valid_from_mixed::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, 0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, true, NULL, NULL);
+}
+
+bool gen_rct_tx_pre_rct_bad_real_dest::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ bool tx_creation_succeeded = false;
+ // in the case, the tx will fail to create, due to mismatched sk/pk
+ bool ret = generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[0].second.dest);},
+ [&tx_creation_succeeded](const transaction &tx){tx_creation_succeeded=true;});
+ return !ret && !tx_creation_succeeded;
+}
+
+bool gen_rct_tx_pre_rct_bad_real_mask::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);},
+ NULL);
+}
+
+bool gen_rct_tx_pre_rct_bad_fake_dest::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[1].second.dest);},
+ NULL);
+}
+
+bool gen_rct_tx_pre_rct_bad_fake_mask::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);},
+ NULL);
+}
+
+bool gen_rct_tx_rct_bad_real_dest::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ bool tx_creation_succeeded = false;
+ // in the case, the tx will fail to create, due to mismatched sk/pk
+ bool ret = generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[0].second.dest);},
+ [&tx_creation_succeeded](const transaction &tx){tx_creation_succeeded=true;});
+ return !ret && !tx_creation_succeeded;
+}
+
+bool gen_rct_tx_rct_bad_real_mask::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(99999);},
+ NULL);
+}
+
+bool gen_rct_tx_rct_bad_fake_dest::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {rct::key sk; rct::skpkGen(sk, sources[0].outputs[1].second.dest);},
+ NULL);
+}
+
+bool gen_rct_tx_rct_bad_fake_mask::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[1].second.mask = rct::zeroCommit(99999);},
+ NULL);
+}
+
+bool gen_rct_tx_rct_spend_with_zero_commit::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ [](std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations) {sources[0].outputs[0].second.mask = rct::zeroCommit(sources[0].amount); sources[0].mask = rct::identity();},
+ [](transaction &tx){boost::get<txin_to_key>(tx.vin[0]).amount = 0;});
+}
+
+bool gen_rct_tx_pre_rct_zero_vin_amount::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {boost::get<txin_to_key>(tx.vin[0]).amount = 0;});
+}
+
+bool gen_rct_tx_rct_non_zero_vin_amount::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {boost::get<txin_to_key>(tx.vin[0]).amount = 5000000000000;}); // one that we know exists
+}
+
+bool gen_rct_tx_non_zero_vout_amount::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.vout[0].amount = 5000000000000;}); // one that we know exists
+}
+
+bool gen_rct_tx_pre_rct_duplicate_key_image::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [&events](transaction &tx) {boost::get<txin_to_key>(tx.vin[0]).k_image = boost::get<txin_to_key>(boost::get<transaction>(events[67]).vin[0]).k_image;});
+}
+
+bool gen_rct_tx_rct_duplicate_key_image::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [&events](transaction &tx) {boost::get<txin_to_key>(tx.vin[0]).k_image = boost::get<txin_to_key>(boost::get<transaction>(events[67]).vin[0]).k_image;});
+}
+
+bool gen_rct_tx_pre_rct_wrong_key_image::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ // some random key image from the monero blockchain, so we get something that is a valid key image
+ static const uint8_t k_image[33] = "\x49\x3b\x56\x16\x54\x76\xa8\x75\xb7\xf4\xa8\x51\xf5\x55\xd3\x44\xe7\x3e\xea\x73\xee\xc1\x06\x7c\x7d\xb6\x57\x28\x46\x85\xe1\x07";
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {memcpy(&boost::get<txin_to_key>(tx.vin[0]).k_image, k_image, 32);});
+}
+
+bool gen_rct_tx_rct_wrong_key_image::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ // some random key image from the monero blockchain, so we get something that is a valid key image
+ static const uint8_t k_image[33] = "\x49\x3b\x56\x16\x54\x76\xa8\x75\xb7\xf4\xa8\x51\xf5\x55\xd3\x44\xe7\x3e\xea\x73\xee\xc1\x06\x7c\x7d\xb6\x57\x28\x46\x85\xe1\x07";
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {memcpy(&boost::get<txin_to_key>(tx.vin[0]).k_image, k_image, 32);});
+}
+
+bool gen_rct_tx_pre_rct_wrong_fee::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.rct_signatures.txnFee++;});
+}
+
+bool gen_rct_tx_rct_wrong_fee::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.rct_signatures.txnFee++;});
+}
+
+bool gen_rct_tx_pre_rct_increase_vin_and_fee::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {boost::get<txin_to_key>(tx.vin[0]).amount++;tx.rct_signatures.txnFee++;});
+}
+
+bool gen_rct_tx_pre_rct_remove_vin::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.vin.pop_back();});
+}
+
+bool gen_rct_tx_rct_remove_vin::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.vin.pop_back();});
+}
+
+bool gen_rct_tx_pre_rct_add_vout::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.vout.push_back(tx.vout.back());});
+}
+
+bool gen_rct_tx_rct_add_vout::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {tx.vout.push_back(tx.vout.back());});
+}
+
+bool gen_rct_tx_pre_rct_altered_extra::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {0, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);});
+}
+
+bool gen_rct_tx_rct_altered_extra::generate(std::vector<test_event_entry>& events) const
+{
+ const int mixin = 2;
+ const int out_idx[] = {1, -1};
+ const uint64_t amount_paid = 10000;
+ return generate_with(events, out_idx, mixin, amount_paid, false,
+ NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);});
+}
+
diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h
new file mode 100644
index 000000000..f16e665f9
--- /dev/null
+++ b/tests/core_tests/rct.h
@@ -0,0 +1,264 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+#include "chaingen.h"
+
+struct gen_rct_tx_validation_base : public test_chain_unit_base
+{
+ gen_rct_tx_validation_base()
+ : m_invalid_tx_index(0)
+ , m_invalid_block_index(0)
+ {
+ REGISTER_CALLBACK_METHOD(gen_rct_tx_validation_base, mark_invalid_tx);
+ REGISTER_CALLBACK_METHOD(gen_rct_tx_validation_base, mark_invalid_block);
+ }
+
+ bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/)
+ {
+ if (m_invalid_tx_index == event_idx)
+ return tvc.m_verifivation_failed;
+ else
+ return !tvc.m_verifivation_failed && tx_added;
+ }
+
+ bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/)
+ {
+ if (m_invalid_block_index == event_idx)
+ return bvc.m_verifivation_failed;
+ else
+ return !bvc.m_verifivation_failed;
+ }
+
+ bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
+ {
+ m_invalid_block_index = ev_index + 1;
+ return true;
+ }
+
+ bool mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector<test_event_entry>& /*events*/)
+ {
+ m_invalid_tx_index = ev_index + 1;
+ return true;
+ }
+
+ bool generate_with(std::vector<test_event_entry>& events, const int *out_idx, int mixin,
+ uint64_t amount_paid, bool valid,
+ const std::function<void(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations)> &pre_tx,
+ const std::function<void(cryptonote::transaction &tx)> &post_tx) const;
+
+private:
+ size_t m_invalid_tx_index;
+ size_t m_invalid_block_index;
+};
+
+template<>
+struct get_test_options<gen_rct_tx_validation_base> {
+ const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(0, 0)};
+ const cryptonote::test_options test_options = {
+ hard_forks
+ };
+};
+
+// valid
+struct gen_rct_tx_valid_from_pre_rct : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_valid_from_pre_rct>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_valid_from_rct : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_valid_from_rct>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_valid_from_mixed : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_valid_from_mixed>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// altered commitment/dest
+struct gen_rct_tx_pre_rct_bad_real_dest : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_bad_real_dest>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_bad_real_mask : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_bad_real_mask>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_bad_fake_dest : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_bad_fake_dest>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_bad_fake_mask : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_bad_fake_mask>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_bad_real_dest : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_bad_real_dest>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_bad_real_mask : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_bad_real_mask>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_bad_fake_dest : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_bad_fake_dest>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_bad_fake_mask : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_bad_fake_mask>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_spend_with_zero_commit : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_spend_with_zero_commit>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// altered amounts
+struct gen_rct_tx_pre_rct_zero_vin_amount : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_zero_vin_amount>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_non_zero_vin_amount : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_non_zero_vin_amount>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_non_zero_vout_amount : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_non_zero_vout_amount>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// key image
+struct gen_rct_tx_pre_rct_duplicate_key_image : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_duplicate_key_image>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_duplicate_key_image : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_duplicate_key_image>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_wrong_key_image : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_wrong_key_image>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_wrong_key_image : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_wrong_key_image>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// fee
+struct gen_rct_tx_pre_rct_wrong_fee : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_wrong_fee>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_wrong_fee : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_wrong_fee>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_increase_vin_and_fee : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_increase_vin_and_fee>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// modify vin/vout
+struct gen_rct_tx_pre_rct_remove_vin : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_remove_vin>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_remove_vin : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_remove_vin>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_pre_rct_add_vout : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_add_vout>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_add_vout : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_add_vout>: public get_test_options<gen_rct_tx_validation_base> {};
+
+// extra
+struct gen_rct_tx_pre_rct_altered_extra : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_pre_rct_altered_extra>: public get_test_options<gen_rct_tx_validation_base> {};
+
+struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base
+{
+ bool generate(std::vector<test_event_entry>& events) const;
+};
+template<> struct get_test_options<gen_rct_tx_rct_altered_extra>: public get_test_options<gen_rct_tx_validation_base> {};
+
diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp
index 5c866b618..cb585b975 100644
--- a/tests/core_tests/transaction_tests.cpp
+++ b/tests/core_tests/transaction_tests.cpp
@@ -82,32 +82,22 @@ bool test_transaction_generation_and_ring_signature()
src.amount = 70368744177663;
{
tx_output_entry oe;
- oe.first = 0;
- oe.second = boost::get<txout_to_key>(tx_mine_1.vout[0].target).key;
- src.outputs.push_back(oe);
- oe.first = 1;
- oe.second = boost::get<txout_to_key>(tx_mine_2.vout[0].target).key;
- src.outputs.push_back(oe);
+ src.push_output(0, boost::get<txout_to_key>(tx_mine_1.vout[0].target).key, src.amount);
- oe.first = 2;
- oe.second = boost::get<txout_to_key>(tx_mine_3.vout[0].target).key;
- src.outputs.push_back(oe);
+ src.push_output(1, boost::get<txout_to_key>(tx_mine_2.vout[0].target).key, src.amount);
- oe.first = 3;
- oe.second = boost::get<txout_to_key>(tx_mine_4.vout[0].target).key;
- src.outputs.push_back(oe);
+ src.push_output(2, boost::get<txout_to_key>(tx_mine_3.vout[0].target).key, src.amount);
- oe.first = 4;
- oe.second = boost::get<txout_to_key>(tx_mine_5.vout[0].target).key;
- src.outputs.push_back(oe);
+ src.push_output(3, boost::get<txout_to_key>(tx_mine_4.vout[0].target).key, src.amount);
- oe.first = 5;
- oe.second = boost::get<txout_to_key>(tx_mine_6.vout[0].target).key;
- src.outputs.push_back(oe);
+ src.push_output(4, boost::get<txout_to_key>(tx_mine_5.vout[0].target).key, src.amount);
+
+ src.push_output(5, boost::get<txout_to_key>(tx_mine_6.vout[0].target).key, src.amount);
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2);
src.real_output = 1;
+ src.rct = false;
src.real_output_in_tx_index = 0;
}
//fill outputs entry
diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp
index f72c906e5..cf018c8e2 100644
--- a/tests/core_tests/tx_validation.cpp
+++ b/tests/core_tests/tx_validation.cpp
@@ -39,7 +39,7 @@ namespace
{
struct tx_builder
{
- void step1_init(size_t version = CURRENT_TRANSACTION_VERSION, uint64_t unlock_time = 0)
+ void step1_init(size_t version = 1, uint64_t unlock_time = 0)
{
m_tx.vin.clear();
m_tx.vout.clear();
@@ -108,9 +108,13 @@ namespace
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
{
std::vector<const crypto::public_key*> keys_ptrs;
+ std::vector<crypto::public_key> keys(src_entr.outputs.size());
+ size_t j = 0;
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
{
- keys_ptrs.push_back(&o.second);
+ keys[j] = rct::rct2pk(o.second.dest);
+ keys_ptrs.push_back(&keys[j]);
+ ++j;
}
m_tx.signatures.push_back(std::vector<crypto::signature>());
@@ -136,7 +140,7 @@ namespace
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
- builder.step1_init(CURRENT_TRANSACTION_VERSION, unlock_time);
+ builder.step1_init(1, unlock_time);
builder.step2_fill_inputs(from.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
@@ -177,7 +181,7 @@ bool gen_tx_big_version::generate(std::vector<test_event_entry>& events) const
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
tx_builder builder;
- builder.step1_init(CURRENT_TRANSACTION_VERSION + 1, 0);
+ builder.step1_init(1 + 1, 0);
builder.step2_fill_inputs(miner_account.get_keys(), sources);
builder.step3_fill_outputs(destinations);
builder.step4_calc_hash();
diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp
index fe6b8b279..93ddd8a12 100644
--- a/tests/core_tests/v2_tests.cpp
+++ b/tests/core_tests/v2_tests.cpp
@@ -79,7 +79,6 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
}
// create a tx with the Nth outputs of miner's block reward
- typedef tx_source_entry::output_entry tx_output_entry;
std::vector<tx_source_entry> sources;
for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) {
sources.resize(sources.size()+1);
@@ -88,16 +87,16 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
src.amount = blocks[0].miner_tx.vout[out_idx[out_idx_idx]].amount;
std::cout << "using " << print_money(src.amount) << " output at index " << out_idx[out_idx_idx] << std::endl;
for (int m = 0; m <= mixin; ++m) {
- tx_output_entry oe;
+ int idx;
if (is_valid_decomposed_amount(src.amount))
- oe.first = m+1; // one out of that size per miner tx, including genesis
+ idx = m+1; // one out of that size per miner tx, including genesis
else
- oe.first = 0; // dusty, no other output of that size
- oe.second = boost::get<txout_to_key>(blocks[m].miner_tx.vout[out_idx[out_idx_idx]].target).key;
- src.outputs.push_back(oe);
+ idx = 0; // dusty, no other output of that size
+ src.push_output(idx, boost::get<txout_to_key>(blocks[m].miner_tx.vout[out_idx[out_idx_idx]].target).key, src.amount);
}
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[0].miner_tx);
src.real_output = 0;
+ src.rct = false;
src.real_output_in_tx_index = out_idx[out_idx_idx];
}
diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h
index 10049ec95..fbc2b5295 100644
--- a/tests/core_tests/v2_tests.h
+++ b/tests/core_tests/v2_tests.h
@@ -79,7 +79,7 @@ private:
template<>
struct get_test_options<gen_v2_tx_validation_base> {
- const std::pair<uint8_t, uint64_t> hard_forks[2] = {std::make_pair(1, 0), std::make_pair(2, 1)};
+ const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(0, 0)};
const cryptonote::test_options test_options = {
hard_forks
};
diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp
index 6bf910101..585328348 100644
--- a/tests/functional_tests/transactions_flow_test.cpp
+++ b/tests/functional_tests/transactions_flow_test.cpp
@@ -281,7 +281,7 @@ bool transactions_flow_test(std::string& working_folder,
w2.get_transfers(tc);
BOOST_FOREACH(tools::wallet2::transfer_details& td, tc)
{
- auto it = txs.find(get_transaction_hash(td.m_tx));
+ auto it = txs.find(td.m_txid);
CHECK_AND_ASSERT_MES(it != txs.end(), false, "transaction not found in local cache");
it->second.m_received_count += 1;
}
diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp
index c076776c4..63ff0343b 100644
--- a/tests/functional_tests/transactions_generation_from_blockchain.cpp
+++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp
@@ -31,7 +31,6 @@
#include "include_base_utils.h"
using namespace epee;
#include "wallet/wallet2.h"
-#include "cryptonote_core/blockchain_storage.h"
using namespace cryptonote;
@@ -114,6 +113,7 @@ bool make_tx(blockchain_storage& bch)
src.real_out_tx_key = td.m_tx.tx_pub_key;
src.real_output = interted_it - src.outputs.begin();
src.real_output_in_tx_index = td.m_internal_output_index;
+ src.rct = false;
++i;
}
diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h
index c28e8cad4..d8898b60d 100644
--- a/tests/performance_tests/multi_tx_test_base.h
+++ b/tests/performance_tests/multi_tx_test_base.h
@@ -59,7 +59,7 @@ public:
return false;
txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target);
- output_entries.push_back(std::make_pair(i, tx_out.key));
+ output_entries.push_back(std::make_pair(i, rct::ctkey({rct::pk2rct(tx_out.key), rct::identity()})));
m_public_keys[i] = tx_out.key;
m_public_key_ptrs[i] = &m_public_keys[i];
}
@@ -72,6 +72,7 @@ public:
source_entry.real_output_in_tx_index = 0;
source_entry.outputs.swap(output_entries);
source_entry.real_output = real_source_idx;
+ source_entry.rct = false;
m_sources.push_back(source_entry);
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 1b272cf5a..3d42809e3 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -51,7 +51,9 @@ set(unit_tests_sources
test_protocol_pack.cpp
hardfork.cpp
unbound.cpp
- varint.cpp)
+ varint.cpp
+ ringct.cpp
+ output_selection.cpp)
set(unit_tests_headers
unit_tests_utils.h)
@@ -61,6 +63,7 @@ add_executable(unit_tests
${unit_tests_headers})
target_link_libraries(unit_tests
LINK_PRIVATE
+ ringct
cryptonote_core
blockchain_db
rpc
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index eab6b9cf8..a279b6c68 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -96,7 +96,7 @@ public:
virtual void remove_block() { blocks.pop_back(); }
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) {return 0;}
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) {}
- virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) {return 0;}
+ virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;}
virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {}
virtual void add_spent_key(const crypto::key_image& k_image) {}
virtual void remove_spent_key(const crypto::key_image& k_image) {}
diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp
new file mode 100644
index 000000000..4344d1ffc
--- /dev/null
+++ b/tests/unit_tests/output_selection.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// FIXME: move this into a full wallet2 unit test suite, if possible
+
+#include "gtest/gtest.h"
+
+#include "wallet/wallet2.h"
+#include <string>
+
+static tools::wallet2::transfer_container make_transfers_container(size_t N)
+{
+ tools::wallet2::transfer_container transfers;
+ for (size_t n = 0; n < N; ++n)
+ {
+ transfers.push_back(AUTO_VAL_INIT(tools::wallet2::transfer_details()));
+ tools::wallet2::transfer_details &td = transfers.back();
+ td.m_block_height = 1000;
+ td.m_spent = false;
+ td.m_txid = cryptonote::null_hash;
+ td.m_txid.data[0] = n & 0xff;
+ td.m_txid.data[1] = (n >> 8) & 0xff;
+ td.m_txid.data[2] = (n >> 16) & 0xff;
+ td.m_txid.data[3] = (n >> 24) & 0xff;
+ }
+ return transfers;
+}
+
+#define SELECT(idx) \
+ do { \
+ auto i = std::find(unused_indices.begin(), unused_indices.end(), idx); \
+ ASSERT_TRUE(i != unused_indices.end()); \
+ unused_indices.erase(i); \
+ selected.push_back(transfers.begin() + idx); \
+ } while(0)
+
+#define PICK(expected) \
+ do { \
+ size_t idx = w.pop_best_value_from(transfers, unused_indices, selected); \
+ ASSERT_EQ(expected, idx); \
+ selected.push_back(transfers.begin() + idx); \
+ } while(0)
+
+TEST(select_outputs, one_out_of_N)
+{
+ tools::wallet2 w;
+
+ // check that if there are N-1 outputs of the same height, one of them
+ // already selected, the next one selected is the one that's from a
+ // different height
+ tools::wallet2::transfer_container transfers = make_transfers_container(10);
+ transfers[6].m_block_height = 700;
+ std::vector<size_t> unused_indices({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
+ std::list<tools::wallet2::transfer_container::iterator> selected;
+ SELECT(2);
+ PICK(6);
+}
+
+TEST(select_outputs, order)
+{
+ tools::wallet2 w;
+
+ // check that most unrelated heights are picked in order
+ tools::wallet2::transfer_container transfers = make_transfers_container(5);
+ transfers[0].m_block_height = 700;
+ transfers[1].m_block_height = 700;
+ transfers[2].m_block_height = 704;
+ transfers[3].m_block_height = 716;
+ transfers[4].m_block_height = 701;
+ std::vector<size_t> unused_indices({0, 1, 2, 3, 4});
+ std::list<tools::wallet2::transfer_container::iterator> selected;
+ SELECT(0);
+ PICK(3); // first the one that's far away
+ PICK(2); // then the one that's close
+ PICK(4); // then the one that's adjacent
+ PICK(1); // then the one that's on the same height
+}
+
diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp
new file mode 100644
index 000000000..224e32e61
--- /dev/null
+++ b/tests/unit_tests/ringct.cpp
@@ -0,0 +1,1070 @@
+// Copyright (c) 2014-2016, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "gtest/gtest.h"
+
+#include <cstdint>
+#include <algorithm>
+
+#include "ringct/rctTypes.h"
+#include "ringct/rctSigs.h"
+#include "ringct/rctOps.h"
+
+using namespace crypto;
+using namespace rct;
+
+TEST(ringct, SNL)
+{
+ key x, P1;
+ skpkGen(x, P1);
+
+ key P2 = pkGen();
+ key P3 = pkGen();
+
+ key L1, s1, s2;
+ GenSchnorrNonLinkable(L1, s1, s2, x, P1, P2, 0);
+
+ // a valid one
+ // an invalid one
+ ASSERT_TRUE(VerSchnorrNonLinkable(P1, P2, L1, s1, s2));
+ ASSERT_FALSE(VerSchnorrNonLinkable(P1, P3, L1, s1, s2));
+}
+
+TEST(ringct, ASNL)
+{
+ int j = 0;
+
+ //Tests for ASNL
+ //#ASNL true one, false one, C != sum Ci, and one out of the range..
+ int N = 64;
+ key64 xv;
+ key64 P1v;
+ key64 P2v;
+ bits indi;
+
+ for (j = 0 ; j < N ; j++) {
+ indi[j] = (int)randXmrAmount(2);
+
+ xv[j] = skGen();
+ if ( (int)indi[j] == 0 ) {
+ P1v[j] = scalarmultBase(xv[j]);
+ P2v[j] = pkGen();
+
+ } else {
+
+ P2v[j] = scalarmultBase(xv[j]);
+ P1v[j] = pkGen();
+
+ }
+ }
+
+ //#true one
+ asnlSig L1s2s = GenASNL(xv, P1v, P2v, indi);
+ ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s));
+
+ //#false one
+ indi[3] = (indi[3] + 1) % 2;
+ L1s2s = GenASNL(xv, P1v, P2v, indi);
+ ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s));
+
+ //#true one again
+ indi[3] = (indi[3] + 1) % 2;
+ L1s2s = GenASNL(xv, P1v, P2v, indi);
+ ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s));
+
+ //#false one
+ L1s2s = GenASNL(xv, P2v, P1v, indi);
+ ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s));
+}
+
+TEST(ringct, MG_sigs)
+{
+ int j = 0;
+ int N = 0;
+
+ //Tests for MG Sigs
+ //#MG sig: true one
+ N = 3;// #cols
+ int R = 3;// #rows
+ keyV xtmp = skvGen(R);
+ keyM xm = keyMInit(R, N);// = [[None]*N] #just used to generate test public keys
+ keyV sk = skvGen(R);
+ keyM P = keyMInit(R, N);// = keyM[[None]*N] #stores the public keys;
+ int ind = 2;
+ int i = 0;
+ for (j = 0 ; j < R ; j++) {
+ for (i = 0 ; i < N ; i++)
+ {
+ xm[i][j] = skGen();
+ P[i][j] = scalarmultBase(xm[i][j]);
+ }
+ }
+ for (j = 0 ; j < R ; j++) {
+ sk[j] = xm[ind][j];
+ }
+ key message = identity();
+ mgSig IIccss = MLSAG_Gen(message, P, sk, ind, R);
+ ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, R));
+
+ //#MG sig: false one
+ N = 3;// #cols
+ R = 3;// #rows
+ xtmp = skvGen(R);
+ keyM xx(N, xtmp);// = [[None]*N] #just used to generate test public keys
+ sk = skvGen(R);
+ //P (N, xtmp);// = keyM[[None]*N] #stores the public keys;
+
+ ind = 2;
+ for (j = 0 ; j < R ; j++) {
+ for (i = 0 ; i < N ; i++)
+ {
+ xx[i][j] = skGen();
+ P[i][j] = scalarmultBase(xx[i][j]);
+ }
+ sk[j] = xx[ind][j];
+ }
+ sk[2] = skGen();//asume we don't know one of the private keys..
+ IIccss = MLSAG_Gen(message, P, sk, ind, R);
+ ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, R));
+}
+
+TEST(ringct, range_proofs)
+{
+ //Ring CT Stuff
+ //ct range proofs
+ ctkeyV sc, pc;
+ ctkey sctmp, pctmp;
+ //add fake input 5000
+ tie(sctmp, pctmp) = ctskpkGen(6000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+
+
+ tie(sctmp, pctmp) = ctskpkGen(7000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ vector<xmr_amount >amounts;
+ rct::keyV amount_keys;
+ key mask;
+
+ //add output 500
+ amounts.push_back(500);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ keyV destinations;
+ key Sk, Pk;
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+
+ //add output for 12500
+ amounts.push_back(12500);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+ //compute rct data with mixin 500
+ rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);
+
+ //verify rct data
+ ASSERT_TRUE(verRct(s));
+
+ //decode received amount
+ ASSERT_TRUE(decodeRct(s, amount_keys[1], 1, mask));
+
+ // Ring CT with failing MG sig part should not verify!
+ // Since sum of inputs != outputs
+
+ amounts[1] = 12501;
+ skpkGen(Sk, Pk);
+ destinations[1] = Pk;
+
+
+ //compute rct data with mixin 500
+ s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);
+
+ //verify rct data
+ ASSERT_FALSE(verRct(s));
+
+ //decode received amount
+ ASSERT_TRUE(decodeRct(s, amount_keys[1], 1, mask));
+}
+
+TEST(ringct, range_proofs_with_fee)
+{
+ //Ring CT Stuff
+ //ct range proofs
+ ctkeyV sc, pc;
+ ctkey sctmp, pctmp;
+ //add fake input 5000
+ tie(sctmp, pctmp) = ctskpkGen(6001);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+
+
+ tie(sctmp, pctmp) = ctskpkGen(7000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ vector<xmr_amount >amounts;
+ keyV amount_keys;
+ key mask;
+
+ //add output 500
+ amounts.push_back(500);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ keyV destinations;
+ key Sk, Pk;
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+ //add txn fee for 1
+ //has no corresponding destination..
+ amounts.push_back(1);
+ amount_keys.push_back(hash_to_scalar(zero()));
+
+ //add output for 12500
+ amounts.push_back(12500);
+ amount_keys.push_back(hash_to_scalar(zero()));
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+ //compute rct data with mixin 500
+ rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);
+
+ //verify rct data
+ ASSERT_TRUE(verRct(s));
+
+ //decode received amount
+ ASSERT_TRUE(decodeRct(s, amount_keys[1], 1, mask));
+
+ // Ring CT with failing MG sig part should not verify!
+ // Since sum of inputs != outputs
+
+ amounts[1] = 12501;
+ skpkGen(Sk, Pk);
+ destinations[1] = Pk;
+
+
+ //compute rct data with mixin 500
+ s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);
+
+ //verify rct data
+ ASSERT_FALSE(verRct(s));
+
+ //decode received amount
+ ASSERT_TRUE(decodeRct(s, amount_keys[1], 1, mask));
+}
+
+TEST(ringct, simple)
+{
+ ctkeyV sc, pc;
+ ctkey sctmp, pctmp;
+ //this vector corresponds to output amounts
+ vector<xmr_amount>outamounts;
+ //this vector corresponds to input amounts
+ vector<xmr_amount>inamounts;
+ //this keyV corresponds to destination pubkeys
+ keyV destinations;
+ keyV amount_keys;
+ key mask;
+
+ //add fake input 3000
+ //the sc is secret data
+ //pc is public data
+ tie(sctmp, pctmp) = ctskpkGen(3000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ inamounts.push_back(3000);
+
+ //add fake input 3000
+ //the sc is secret data
+ //pc is public data
+ tie(sctmp, pctmp) = ctskpkGen(3000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ inamounts.push_back(3000);
+
+ //add output 5000
+ outamounts.push_back(5000);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ //add the corresponding destination pubkey
+ key Sk, Pk;
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+ //add output 999
+ outamounts.push_back(999);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ //add the corresponding destination pubkey
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+
+ key message = skGen(); //real message later (hash of txn..)
+
+ //compute sig with mixin 2
+ xmr_amount txnfee = 1;
+
+ rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, amount_keys, txnfee, 2);
+
+ //verify ring ct signature
+ ASSERT_TRUE(verRctSimple(s));
+
+ //decode received amount corresponding to output pubkey index 1
+ ASSERT_TRUE(decodeRctSimple(s, amount_keys[1], 1, mask));
+}
+
+static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], bool last_is_fee)
+{
+ ctkeyV sc, pc;
+ ctkey sctmp, pctmp;
+ vector<xmr_amount >amounts;
+ keyV destinations;
+ keyV amount_keys;
+ key Sk, Pk;
+
+ for (int n = 0; n < n_inputs; ++n) {
+ tie(sctmp, pctmp) = ctskpkGen(input_amounts[n]);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ }
+
+ for (int n = 0; n < n_outputs; ++n) {
+ amounts.push_back(output_amounts[n]);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ skpkGen(Sk, Pk);
+ if (n < n_outputs - 1 || !last_is_fee)
+ destinations.push_back(Pk);
+ }
+
+ return genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);;
+}
+
+static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], uint64_t fee)
+{
+ ctkeyV sc, pc;
+ ctkey sctmp, pctmp;
+ vector<xmr_amount> inamounts, outamounts;
+ keyV destinations;
+ keyV amount_keys;
+ key Sk, Pk;
+
+ for (int n = 0; n < n_inputs; ++n) {
+ inamounts.push_back(input_amounts[n]);
+ tie(sctmp, pctmp) = ctskpkGen(input_amounts[n]);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ }
+
+ for (int n = 0; n < n_outputs; ++n) {
+ outamounts.push_back(output_amounts[n]);
+ amount_keys.push_back(hash_to_scalar(zero()));
+ skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+ }
+
+ return genRctSimple(rct::zero(), sc, pc, destinations, inamounts, outamounts, amount_keys, fee, 3);;
+}
+
+static bool range_proof_test(bool expected_valid,
+ int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], bool last_is_fee, bool simple)
+{
+ //compute rct data
+ bool valid;
+ try {
+ rctSig s;
+ // simple takes fee as a parameter, non-simple takes it as an extra element to output amounts
+ if (simple) {
+ s = make_sample_simple_rct_sig(n_inputs, input_amounts, last_is_fee ? n_outputs - 1 : n_outputs, output_amounts, last_is_fee ? output_amounts[n_outputs - 1] : 0);
+ valid = verRctSimple(s);
+ }
+ else {
+ s = make_sample_rct_sig(n_inputs, input_amounts, n_outputs, output_amounts, last_is_fee);
+ valid = verRct(s);
+ }
+ }
+ catch (const std::exception &e) {
+ valid = false;
+ }
+
+ if (valid == expected_valid) {
+ return testing::AssertionSuccess();
+ }
+ else {
+ return testing::AssertionFailure();
+ }
+}
+
+#define NELTS(array) (sizeof(array)/sizeof(array[0]))
+
+TEST(ringct, range_proofs_reject_empty_outs)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_empty_outs_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_empty_ins)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_empty_ins_simple)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_all_empty)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_all_empty_simple)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_empty)
+{
+ const uint64_t inputs[] = {0};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_empty_simple)
+{
+ const uint64_t inputs[] = {0};
+ const uint64_t outputs[] = {};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_empty_zero)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {0};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_empty_zero_simple)
+{
+ const uint64_t inputs[] = {};
+ const uint64_t outputs[] = {0};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_zero)
+{
+ const uint64_t inputs[] = {0};
+ const uint64_t outputs[] = {0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_zero_simple)
+{
+ const uint64_t inputs[] = {0};
+ const uint64_t outputs[] = {0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_first)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {0, 5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_first_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {0, 5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_last)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5000, 0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_last_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5000, 0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_middle)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {2500, 0, 2500};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_out_middle_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {2500, 0, 2500};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_first)
+{
+ const uint64_t inputs[] = {0, 5000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_first_simple)
+{
+ const uint64_t inputs[] = {0, 5000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_last)
+{
+ const uint64_t inputs[] = {5000, 0};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_last_simple)
+{
+ const uint64_t inputs[] = {5000, 0};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_middle)
+{
+ const uint64_t inputs[] = {2500, 0, 2500};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_zero_in_middle_simple)
+{
+ const uint64_t inputs[] = {2500, 0, 2500};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_single_lower)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_single_lower_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_single_higher)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5001};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_single_higher_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5001};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_single_out_negative)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {(uint64_t)-1000ll};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_single_out_negative_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {(uint64_t)-1000ll};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_first)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {(uint64_t)-1000ll, 6000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_first_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {(uint64_t)-1000ll, 6000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_last)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {6000, (uint64_t)-1000ll};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_last_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {6000, (uint64_t)-1000ll};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_middle)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {3000, (uint64_t)-1000ll, 3000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_out_negative_middle_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {3000, (uint64_t)-1000ll, 3000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_single_in_negative)
+{
+ const uint64_t inputs[] = {(uint64_t)-1000ll};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_single_in_negative_simple)
+{
+ const uint64_t inputs[] = {(uint64_t)-1000ll};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_first)
+{
+ const uint64_t inputs[] = {(uint64_t)-1000ll, 6000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_first_simple)
+{
+ const uint64_t inputs[] = {(uint64_t)-1000ll, 6000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_last)
+{
+ const uint64_t inputs[] = {6000, (uint64_t)-1000ll};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_last_simple)
+{
+ const uint64_t inputs[] = {6000, (uint64_t)-1000ll};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_middle)
+{
+ const uint64_t inputs[] = {3000, (uint64_t)-1000ll, 3000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_in_negative_middle_simple)
+{
+ const uint64_t inputs[] = {3000, (uint64_t)-1000ll, 3000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_reject_higher_list)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_reject_higher_list_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_1_to_1)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_1_to_1_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_1_to_N)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_1_to_N_simple)
+{
+ const uint64_t inputs[] = {5000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false,true));
+}
+
+TEST(ringct, range_proofs_accept_N_to_1)
+{
+ const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_N_to_1_simple)
+{
+ const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000};
+ const uint64_t outputs[] = {5000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_N_to_N)
+{
+ const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_N_to_N_simple)
+{
+ const uint64_t inputs[] = {1000, 1000, 1000, 1000, 1000};
+ const uint64_t outputs[] = {1000, 1000, 1000, 1000, 1000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, range_proofs_accept_very_long)
+{
+ const size_t N=12;
+ uint64_t inputs[N];
+ uint64_t outputs[N];
+ for (size_t n = 0; n < N; ++n) {
+ inputs[n] = n;
+ outputs[n] = n;
+ }
+ std::random_shuffle(inputs, inputs + N);
+ std::random_shuffle(outputs, outputs + N);
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, false));
+}
+
+TEST(ringct, range_proofs_accept_very_long_simple)
+{
+ const size_t N=12;
+ uint64_t inputs[N];
+ uint64_t outputs[N];
+ for (size_t n = 0; n < N; ++n) {
+ inputs[n] = n;
+ outputs[n] = n;
+ }
+ std::random_shuffle(inputs, inputs + N);
+ std::random_shuffle(outputs, outputs + N);
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, false, true));
+}
+
+TEST(ringct, HPow2)
+{
+ key G = scalarmultBase(d2h(1));
+
+ key H = hashToPointSimple(G);
+ for (int j = 0 ; j < ATOMS ; j++) {
+ ASSERT_TRUE(equalKeys(H, H2[j]));
+ addKeys(H, H, H);
+ }
+}
+
+static const xmr_amount test_amounts[]={0, 1, 2, 3, 4, 5, 10000, 10000000000000000000ull, 10203040506070809000ull, 123456789123456789};
+
+TEST(ringct, ecdh_roundtrip)
+{
+ key k;
+ ecdhTuple t0, t1;
+
+ for (auto amount: test_amounts) {
+ skGen(k);
+
+ t0.mask = skGen();
+ t0.amount = d2h(amount);
+
+ t1 = t0;
+ ecdhEncode(t1, k);
+ ecdhDecode(t1, k);
+ ASSERT_TRUE(t0.mask == t1.mask);
+ ASSERT_TRUE(equalKeys(t0.mask, t1.mask));
+ ASSERT_TRUE(t0.amount == t1.amount);
+ ASSERT_TRUE(equalKeys(t0.amount, t1.amount));
+ }
+}
+
+TEST(ringct, d2h)
+{
+ key k, P1;
+ skpkGen(k, P1);
+ for (auto amount: test_amounts) {
+ d2h(k, amount);
+ ASSERT_TRUE(amount == h2d(k));
+ }
+}
+
+TEST(ringct, d2b)
+{
+ for (auto amount: test_amounts) {
+ bits b;
+ d2b(b, amount);
+ ASSERT_TRUE(amount == b2d(b));
+ }
+}
+
+TEST(ringct, prooveRange_is_non_deterministic)
+{
+ key C[2], mask[2];
+ for (int n = 0; n < 2; ++n)
+ proveRange(C[n], mask[n], 80);
+ ASSERT_TRUE(memcmp(C[0].bytes, C[1].bytes, sizeof(C[0].bytes)));
+ ASSERT_TRUE(memcmp(mask[0].bytes, mask[1].bytes, sizeof(mask[0].bytes)));
+}
+
+TEST(ringct, fee_0_valid)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {2000, 0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_0_valid_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {2000, 0};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+TEST(ringct, fee_non_0_valid)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1900, 100};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_non_0_valid_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1900, 100};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+TEST(ringct, fee_non_0_invalid_higher)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1990, 100};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_non_0_invalid_higher_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1990, 100};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+TEST(ringct, fee_non_0_invalid_lower)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1000, 100};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_non_0_invalid_lower_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1000, 100};
+ EXPECT_TRUE(range_proof_test(false, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+TEST(ringct, fee_burn_valid_one_out)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {0, 2000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_burn_valid_one_out_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {0, 2000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+TEST(ringct, fee_burn_valid_zero_out)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {2000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, false));
+}
+
+TEST(ringct, fee_burn_valid_zero_out_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {2000};
+ EXPECT_TRUE(range_proof_test(true, NELTS(inputs), inputs, NELTS(outputs), outputs, true, true));
+}
+
+#define TEST_rctSig_elements(name, op) \
+TEST(ringct, rctSig_##name) \
+{ \
+ const uint64_t inputs[] = {1000, 1000}; \
+ const uint64_t outputs[] = {1000, 1000}; \
+ rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true); \
+ ASSERT_TRUE(rct::verRct(sig)); \
+ op; \
+ ASSERT_FALSE(rct::verRct(sig)); \
+}
+
+TEST_rctSig_elements(rangeSigs_empty, sig.p.rangeSigs.resize(0));
+TEST_rctSig_elements(rangeSigs_too_many, sig.p.rangeSigs.push_back(sig.p.rangeSigs.back()));
+TEST_rctSig_elements(rangeSigs_too_few, sig.p.rangeSigs.pop_back());
+TEST_rctSig_elements(mgSig_MG_empty, sig.p.MGs.resize(0));
+TEST_rctSig_elements(mgSig_ss_empty, sig.p.MGs[0].ss.resize(0));
+TEST_rctSig_elements(mgSig_ss_too_many, sig.p.MGs[0].ss.push_back(sig.p.MGs[0].ss.back()));
+TEST_rctSig_elements(mgSig_ss_too_few, sig.p.MGs[0].ss.pop_back());
+TEST_rctSig_elements(mgSig_ss0_empty, sig.p.MGs[0].ss[0].resize(0));
+TEST_rctSig_elements(mgSig_ss0_too_many, sig.p.MGs[0].ss[0].push_back(sig.p.MGs[0].ss[0].back()));
+TEST_rctSig_elements(mgSig_ss0_too_few, sig.p.MGs[0].ss[0].pop_back());
+TEST_rctSig_elements(mgSig_II_empty, sig.p.MGs[0].II.resize(0));
+TEST_rctSig_elements(mgSig_II_too_many, sig.p.MGs[0].II.push_back(sig.p.MGs[0].II.back()));
+TEST_rctSig_elements(mgSig_II_too_few, sig.p.MGs[0].II.pop_back());
+TEST_rctSig_elements(mixRing_empty, sig.mixRing.resize(0));
+TEST_rctSig_elements(mixRing_too_many, sig.mixRing.push_back(sig.mixRing.back()));
+TEST_rctSig_elements(mixRing_too_few, sig.mixRing.pop_back());
+TEST_rctSig_elements(mixRing0_empty, sig.mixRing[0].resize(0));
+TEST_rctSig_elements(mixRing0_too_many, sig.mixRing[0].push_back(sig.mixRing[0].back()));
+TEST_rctSig_elements(mixRing0_too_few, sig.mixRing[0].pop_back());
+TEST_rctSig_elements(ecdhInfo_empty, sig.ecdhInfo.resize(0));
+TEST_rctSig_elements(ecdhInfo_too_many, sig.ecdhInfo.push_back(sig.ecdhInfo.back()));
+TEST_rctSig_elements(ecdhInfo_too_few, sig.ecdhInfo.pop_back());
+TEST_rctSig_elements(outPk_empty, sig.outPk.resize(0));
+TEST_rctSig_elements(outPk_too_many, sig.outPk.push_back(sig.outPk.back()));
+TEST_rctSig_elements(outPk_too_few, sig.outPk.pop_back());
+
+#define TEST_rctSig_elements_simple(name, op) \
+TEST(ringct, rctSig_##name##_simple) \
+{ \
+ const uint64_t inputs[] = {1000, 1000}; \
+ const uint64_t outputs[] = {1000}; \
+ rct::rctSig sig = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 1000); \
+ ASSERT_TRUE(rct::verRctSimple(sig)); \
+ op; \
+ ASSERT_FALSE(rct::verRctSimple(sig)); \
+}
+
+TEST_rctSig_elements_simple(rangeSigs_empty, sig.p.rangeSigs.resize(0));
+TEST_rctSig_elements_simple(rangeSigs_too_many, sig.p.rangeSigs.push_back(sig.p.rangeSigs.back()));
+TEST_rctSig_elements_simple(rangeSigs_too_few, sig.p.rangeSigs.pop_back());
+TEST_rctSig_elements_simple(mgSig_empty, sig.p.MGs.resize(0));
+TEST_rctSig_elements_simple(mgSig_too_many, sig.p.MGs.push_back(sig.p.MGs.back()));
+TEST_rctSig_elements_simple(mgSig_too_few, sig.p.MGs.pop_back());
+TEST_rctSig_elements_simple(mgSig0_ss_empty, sig.p.MGs[0].ss.resize(0));
+TEST_rctSig_elements_simple(mgSig0_ss_too_many, sig.p.MGs[0].ss.push_back(sig.p.MGs[0].ss.back()));
+TEST_rctSig_elements_simple(mgSig0_ss_too_few, sig.p.MGs[0].ss.pop_back());
+TEST_rctSig_elements_simple(mgSig_ss0_empty, sig.p.MGs[0].ss[0].resize(0));
+TEST_rctSig_elements_simple(mgSig_ss0_too_many, sig.p.MGs[0].ss[0].push_back(sig.p.MGs[0].ss[0].back()));
+TEST_rctSig_elements_simple(mgSig_ss0_too_few, sig.p.MGs[0].ss[0].pop_back());
+TEST_rctSig_elements_simple(mgSig0_II_empty, sig.p.MGs[0].II.resize(0));
+TEST_rctSig_elements_simple(mgSig0_II_too_many, sig.p.MGs[0].II.push_back(sig.p.MGs[0].II.back()));
+TEST_rctSig_elements_simple(mgSig0_II_too_few, sig.p.MGs[0].II.pop_back());
+TEST_rctSig_elements_simple(mixRing_empty, sig.mixRing.resize(0));
+TEST_rctSig_elements_simple(mixRing_too_many, sig.mixRing.push_back(sig.mixRing.back()));
+TEST_rctSig_elements_simple(mixRing_too_few, sig.mixRing.pop_back());
+TEST_rctSig_elements_simple(mixRing0_empty, sig.mixRing[0].resize(0));
+TEST_rctSig_elements_simple(mixRing0_too_many, sig.mixRing[0].push_back(sig.mixRing[0].back()));
+TEST_rctSig_elements_simple(mixRing0_too_few, sig.mixRing[0].pop_back());
+TEST_rctSig_elements_simple(pseudoOuts_empty, sig.pseudoOuts.resize(0));
+TEST_rctSig_elements_simple(pseudoOuts_too_many, sig.pseudoOuts.push_back(sig.pseudoOuts.back()));
+TEST_rctSig_elements_simple(pseudoOuts_too_few, sig.pseudoOuts.pop_back());
+TEST_rctSig_elements_simple(ecdhInfo_empty, sig.ecdhInfo.resize(0));
+TEST_rctSig_elements_simple(ecdhInfo_too_many, sig.ecdhInfo.push_back(sig.ecdhInfo.back()));
+TEST_rctSig_elements_simple(ecdhInfo_too_few, sig.ecdhInfo.pop_back());
+TEST_rctSig_elements_simple(outPk_empty, sig.outPk.resize(0));
+TEST_rctSig_elements_simple(outPk_too_many, sig.outPk.push_back(sig.outPk.back()));
+TEST_rctSig_elements_simple(outPk_too_few, sig.outPk.pop_back());
+
+TEST(ringct, reject_gen_simple_ver_non_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1000};
+ rct::rctSig sig = make_sample_simple_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, 1000);
+ ASSERT_FALSE(rct::verRct(sig));
+}
+
+TEST(ringct, reject_gen_non_simple_ver_simple)
+{
+ const uint64_t inputs[] = {1000, 1000};
+ const uint64_t outputs[] = {1000, 1000};
+ rct::rctSig sig = make_sample_rct_sig(NELTS(inputs), inputs, NELTS(outputs), outputs, true);
+ ASSERT_FALSE(rct::verRctSimple(sig));
+}
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index b110a41ab..40209f6ed 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -36,6 +36,7 @@
#include <boost/foreach.hpp>
#include "cryptonote_core/cryptonote_basic.h"
#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "ringct/rctSigs.h"
#include "serialization/serialization.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
@@ -442,3 +443,200 @@ TEST(Serialization, serializes_transacion_signatures_correctly)
blob.resize(blob.size() + sizeof(crypto::signature) / 2);
ASSERT_FALSE(serialization::parse_binary(blob, tx1));
}
+
+TEST(Serialization, serializes_ringct_types)
+{
+ string blob;
+ rct::key key0, key1;
+ rct::keyV keyv0, keyv1;
+ rct::keyM keym0, keym1;
+ rct::ctkey ctkey0, ctkey1;
+ rct::ctkeyV ctkeyv0, ctkeyv1;
+ rct::ctkeyM ctkeym0, ctkeym1;
+ rct::ecdhTuple ecdh0, ecdh1;
+ rct::asnlSig asnl0, asnl1;
+ rct::mgSig mg0, mg1;
+ rct::rangeSig rg0, rg1;
+ rct::rctSig s0, s1;
+ cryptonote::transaction tx0, tx1;
+
+ key0 = rct::skGen();
+ ASSERT_TRUE(serialization::dump_binary(key0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, key1));
+ ASSERT_TRUE(key0 == key1);
+
+ keyv0 = rct::skvGen(30);
+ for (size_t n = 0; n < keyv0.size(); ++n)
+ keyv0[n] = rct::skGen();
+ ASSERT_TRUE(serialization::dump_binary(keyv0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, keyv1));
+ ASSERT_TRUE(keyv0.size() == keyv1.size());
+ for (size_t n = 0; n < keyv0.size(); ++n)
+ {
+ ASSERT_TRUE(keyv0[n] == keyv1[n]);
+ }
+
+ keym0 = rct::keyMInit(9, 12);
+ for (size_t n = 0; n < keym0.size(); ++n)
+ for (size_t i = 0; i < keym0[n].size(); ++i)
+ keym0[n][i] = rct::skGen();
+ ASSERT_TRUE(serialization::dump_binary(keym0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, keym1));
+ ASSERT_TRUE(keym0.size() == keym1.size());
+ for (size_t n = 0; n < keym0.size(); ++n)
+ {
+ ASSERT_TRUE(keym0[n].size() == keym1[n].size());
+ for (size_t i = 0; i < keym0[n].size(); ++i)
+ {
+ ASSERT_TRUE(keym0[n][i] == keym1[n][i]);
+ }
+ }
+
+ rct::skpkGen(ctkey0.dest, ctkey0.mask);
+ ASSERT_TRUE(serialization::dump_binary(ctkey0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, ctkey1));
+ ASSERT_TRUE(!memcmp(&ctkey0, &ctkey1, sizeof(ctkey0)));
+
+ ctkeyv0 = std::vector<rct::ctkey>(14);
+ for (size_t n = 0; n < ctkeyv0.size(); ++n)
+ rct::skpkGen(ctkeyv0[n].dest, ctkeyv0[n].mask);
+ ASSERT_TRUE(serialization::dump_binary(ctkeyv0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, ctkeyv1));
+ ASSERT_TRUE(ctkeyv0.size() == ctkeyv1.size());
+ for (size_t n = 0; n < ctkeyv0.size(); ++n)
+ {
+ ASSERT_TRUE(!memcmp(&ctkeyv0[n], &ctkeyv1[n], sizeof(ctkeyv0[n])));
+ }
+
+ ctkeym0 = std::vector<rct::ctkeyV>(9);
+ for (size_t n = 0; n < ctkeym0.size(); ++n)
+ {
+ ctkeym0[n] = std::vector<rct::ctkey>(11);
+ for (size_t i = 0; i < ctkeym0[n].size(); ++i)
+ rct::skpkGen(ctkeym0[n][i].dest, ctkeym0[n][i].mask);
+ }
+ ASSERT_TRUE(serialization::dump_binary(ctkeym0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, ctkeym1));
+ ASSERT_TRUE(ctkeym0.size() == ctkeym1.size());
+ for (size_t n = 0; n < ctkeym0.size(); ++n)
+ {
+ ASSERT_TRUE(ctkeym0[n].size() == ctkeym1[n].size());
+ for (size_t i = 0; i < ctkeym0.size(); ++i)
+ {
+ ASSERT_TRUE(!memcmp(&ctkeym0[n][i], &ctkeym1[n][i], sizeof(ctkeym0[n][i])));
+ }
+ }
+
+ ecdh0.mask = rct::skGen();
+ ecdh0.amount = rct::skGen();
+ ecdh0.senderPk = rct::skGen();
+ ASSERT_TRUE(serialization::dump_binary(ecdh0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, ecdh1));
+ ASSERT_TRUE(!memcmp(&ecdh0.mask, &ecdh1.mask, sizeof(ecdh0.mask)));
+ ASSERT_TRUE(!memcmp(&ecdh0.amount, &ecdh1.amount, sizeof(ecdh0.amount)));
+ // senderPk is not serialized
+
+ for (size_t n = 0; n < 64; ++n)
+ {
+ asnl0.L1[n] = rct::skGen();
+ asnl0.s2[n] = rct::skGen();
+ }
+ asnl0.s = rct::skGen();
+ ASSERT_TRUE(serialization::dump_binary(asnl0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, asnl1));
+ ASSERT_TRUE(!memcmp(&asnl0, &asnl1, sizeof(asnl0)));
+
+ // create a full rct signature to use its innards
+ rct::ctkeyV sc, pc;
+ rct::ctkey sctmp, pctmp;
+ tie(sctmp, pctmp) = rct::ctskpkGen(6000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ tie(sctmp, pctmp) = rct::ctskpkGen(7000);
+ sc.push_back(sctmp);
+ pc.push_back(pctmp);
+ vector<uint64_t> amounts;
+ rct::keyV amount_keys;
+ //add output 500
+ amounts.push_back(500);
+ rct::keyV destinations;
+ rct::key Sk, Pk;
+ rct::skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+ //add output for 12500
+ amounts.push_back(12500);
+ amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
+ rct::skpkGen(Sk, Pk);
+ destinations.push_back(Pk);
+ //compute rct data with mixin 500
+ s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);
+
+ mg0 = s0.p.MGs[0];
+ ASSERT_TRUE(serialization::dump_binary(mg0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, mg1));
+ ASSERT_TRUE(mg0.ss.size() == mg1.ss.size());
+ for (size_t n = 0; n < mg0.ss.size(); ++n)
+ {
+ ASSERT_TRUE(mg0.ss[n] == mg1.ss[n]);
+ }
+ ASSERT_TRUE(mg0.cc == mg1.cc);
+
+ // mixRing and II are not serialized, they are meant to be reconstructed
+ ASSERT_TRUE(mg1.II.empty());
+
+ rg0 = s0.p.rangeSigs.front();
+ ASSERT_TRUE(serialization::dump_binary(rg0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, rg1));
+ ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0)));
+
+ ASSERT_TRUE(serialization::dump_binary(s0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, s1));
+ ASSERT_TRUE(s0.type == s1.type);
+ ASSERT_TRUE(s0.p.rangeSigs.size() == s1.p.rangeSigs.size());
+ for (size_t n = 0; n < s0.p.rangeSigs.size(); ++n)
+ {
+ ASSERT_TRUE(!memcmp(&s0.p.rangeSigs[n], &s1.p.rangeSigs[n], sizeof(s0.p.rangeSigs[n])));
+ }
+ ASSERT_TRUE(s0.p.MGs.size() == s1.p.MGs.size());
+ ASSERT_TRUE(s0.p.MGs[0].ss.size() == s1.p.MGs[0].ss.size());
+ for (size_t n = 0; n < s0.p.MGs[0].ss.size(); ++n)
+ {
+ ASSERT_TRUE(s0.p.MGs[0].ss[n] == s1.p.MGs[0].ss[n]);
+ }
+ ASSERT_TRUE(s0.p.MGs[0].cc == s1.p.MGs[0].cc);
+ // mixRing and II are not serialized, they are meant to be reconstructed
+ ASSERT_TRUE(s1.p.MGs[0].II.empty());
+
+ // mixRing and II are not serialized, they are meant to be reconstructed
+ ASSERT_TRUE(s1.mixRing.size() == 0);
+
+ ASSERT_TRUE(s0.ecdhInfo.size() == s1.ecdhInfo.size());
+ for (size_t n = 0; n < s0.ecdhInfo.size(); ++n)
+ {
+ ASSERT_TRUE(!memcmp(&s0.ecdhInfo[n], &s1.ecdhInfo[n], sizeof(s0.ecdhInfo[n])));
+ }
+ ASSERT_TRUE(s0.outPk.size() == s1.outPk.size());
+ for (size_t n = 0; n < s0.outPk.size(); ++n)
+ {
+ // serialization only does the mask
+ ASSERT_TRUE(!memcmp(&s0.outPk[n].mask, &s1.outPk[n].mask, sizeof(s0.outPk[n].mask)));
+ }
+
+ tx0.set_null();
+ tx0.version = 2;
+ cryptonote::txin_to_key txin_to_key1;
+ txin_to_key1.key_offsets.resize(2);
+ cryptonote::txin_to_key txin_to_key2;
+ txin_to_key2.key_offsets.resize(2);
+ tx0.vin.push_back(txin_to_key1);
+ tx0.vin.push_back(txin_to_key2);
+ tx0.vout.push_back(cryptonote::tx_out());
+ tx0.rct_signatures = s0;
+ ASSERT_EQ(tx0.rct_signatures.p.rangeSigs.size(), 2);
+ ASSERT_TRUE(serialization::dump_binary(tx0, blob));
+ ASSERT_TRUE(serialization::parse_binary(blob, tx1));
+ ASSERT_EQ(tx1.rct_signatures.p.rangeSigs.size(), 2);
+ std::string blob2;
+ ASSERT_TRUE(serialization::dump_binary(tx1, blob2));
+ ASSERT_TRUE(blob == blob2);
+}
diff --git a/utils/conf/bitmonerod.conf b/utils/conf/bitmonerod.conf
new file mode 100644
index 000000000..64a02741d
--- /dev/null
+++ b/utils/conf/bitmonerod.conf
@@ -0,0 +1,7 @@
+# Configuration for bitmonerod
+# Syntax: any command line option may be specified as 'clioptionname=value'.
+# See 'bitmonerod --help' for all available options.
+
+data-dir=/var/lib/bitmonero
+log-file=/var/log/bitmonero/bitmonero.log
+log-level=0
diff --git a/utils/systemd/bitmonerod.service b/utils/systemd/bitmonerod.service
new file mode 100644
index 000000000..da012c4e3
--- /dev/null
+++ b/utils/systemd/bitmonerod.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=Monero cryptocurrency node
+After=network.target
+
+[Service]
+User=bitmonero
+Group=bitmonero
+WorkingDirectory=~
+
+Type=forking
+ExecStart=/usr/bin/bitmonerod --config-file /etc/bitmonerod.conf --detach
+
+# This is necessary because bitmonerod does not yet support
+# writing a PID file, which means systemd tries to guess the PID
+# by default, but it guesses wrong (sometimes, depending on
+# random timing of events), because the daemon forks twice.
+# The ultimate fix is for the daemon to write a PID file, and
+# a workaround is to disable the guessing feature in systemd.
+GuessMainPID=no
+
+[Install]
+WantedBy=multi-user.target