2016-01-18 15:33:29 +00:00
|
|
|
// Copyright (c) 2011-2016 The Cryptonote developers
|
2015-09-18 11:55:31 +00:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
#include "Currency.h"
|
2015-05-27 12:08:46 +00:00
|
|
|
#include <cctype>
|
2014-08-13 10:51:37 +00:00
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
2015-07-30 15:22:07 +00:00
|
|
|
#include "../Common/Base58.h"
|
2015-05-27 12:08:46 +00:00
|
|
|
#include "../Common/int-util.h"
|
2015-07-30 15:22:07 +00:00
|
|
|
#include "../Common/StringTools.h"
|
|
|
|
|
|
|
|
#include "Account.h"
|
|
|
|
#include "CryptoNoteBasicImpl.h"
|
|
|
|
#include "CryptoNoteFormatUtils.h"
|
|
|
|
#include "CryptoNoteTools.h"
|
|
|
|
#include "TransactionExtra.h"
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
#undef ERROR
|
|
|
|
|
|
|
|
using namespace Logging;
|
2015-07-30 15:22:07 +00:00
|
|
|
using namespace Common;
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
namespace CryptoNote {
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
const std::vector<uint64_t> Currency::PRETTY_AMOUNTS = {
|
|
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
|
|
10, 20, 30, 40, 50, 60, 70, 80, 90,
|
|
|
|
100, 200, 300, 400, 500, 600, 700, 800, 900,
|
|
|
|
1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,
|
|
|
|
10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000,
|
|
|
|
100000, 200000, 300000, 400000, 500000, 600000, 700000, 800000, 900000,
|
|
|
|
1000000, 2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000,
|
|
|
|
10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000,
|
|
|
|
100000000, 200000000, 300000000, 400000000, 500000000, 600000000, 700000000, 800000000, 900000000,
|
|
|
|
1000000000, 2000000000, 3000000000, 4000000000, 5000000000, 6000000000, 7000000000, 8000000000, 9000000000,
|
|
|
|
10000000000, 20000000000, 30000000000, 40000000000, 50000000000, 60000000000, 70000000000, 80000000000, 90000000000,
|
|
|
|
100000000000, 200000000000, 300000000000, 400000000000, 500000000000, 600000000000, 700000000000, 800000000000, 900000000000,
|
|
|
|
1000000000000, 2000000000000, 3000000000000, 4000000000000, 5000000000000, 6000000000000, 7000000000000, 8000000000000, 9000000000000,
|
|
|
|
10000000000000, 20000000000000, 30000000000000, 40000000000000, 50000000000000, 60000000000000, 70000000000000, 80000000000000, 90000000000000,
|
|
|
|
100000000000000, 200000000000000, 300000000000000, 400000000000000, 500000000000000, 600000000000000, 700000000000000, 800000000000000, 900000000000000,
|
|
|
|
1000000000000000, 2000000000000000, 3000000000000000, 4000000000000000, 5000000000000000, 6000000000000000, 7000000000000000, 8000000000000000, 9000000000000000,
|
|
|
|
10000000000000000, 20000000000000000, 30000000000000000, 40000000000000000, 50000000000000000, 60000000000000000, 70000000000000000, 80000000000000000, 90000000000000000,
|
|
|
|
100000000000000000, 200000000000000000, 300000000000000000, 400000000000000000, 500000000000000000, 600000000000000000, 700000000000000000, 800000000000000000, 900000000000000000,
|
|
|
|
1000000000000000000, 2000000000000000000, 3000000000000000000, 4000000000000000000, 5000000000000000000, 6000000000000000000, 7000000000000000000, 8000000000000000000, 9000000000000000000,
|
|
|
|
10000000000000000000ull
|
|
|
|
};
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::init() {
|
|
|
|
if (!generateGenesisBlock()) {
|
|
|
|
logger(ERROR, BRIGHT_RED) << "Failed to generate genesis block";
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!get_block_hash(m_genesisBlock, m_genesisBlockHash)) {
|
|
|
|
logger(ERROR, BRIGHT_RED) << "Failed to get genesis block hash";
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (isTestnet()) {
|
|
|
|
m_blocksFileName = "testnet_" + m_blocksFileName;
|
|
|
|
m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName;
|
|
|
|
m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName;
|
|
|
|
m_txPoolFileName = "testnet_" + m_txPoolFileName;
|
2015-07-30 15:22:07 +00:00
|
|
|
m_blockchinIndicesFileName = "testnet_" + m_blockchinIndicesFileName;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::generateGenesisBlock() {
|
|
|
|
m_genesisBlock = boost::value_initialized<Block>();
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-09-18 11:55:31 +00:00
|
|
|
// Hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same
|
|
|
|
std::string genesisCoinbaseTxHex = GENESIS_COINBASE_TX_HEX;
|
2015-07-30 15:22:07 +00:00
|
|
|
BinaryArray minerTxBlob;
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool r =
|
2015-07-30 15:22:07 +00:00
|
|
|
fromHex(genesisCoinbaseTxHex, minerTxBlob) &&
|
|
|
|
fromBinaryArray(m_genesisBlock.baseTransaction, minerTxBlob);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!r) {
|
|
|
|
logger(ERROR, BRIGHT_RED) << "failed to parse coinbase tx from hard coded blob";
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
m_genesisBlock.majorVersion = BLOCK_MAJOR_VERSION_1;
|
|
|
|
m_genesisBlock.minorVersion = BLOCK_MINOR_VERSION_0;
|
|
|
|
m_genesisBlock.timestamp = 0;
|
|
|
|
m_genesisBlock.nonce = 70;
|
|
|
|
if (m_testnet) {
|
|
|
|
++m_genesisBlock.nonce;
|
|
|
|
}
|
|
|
|
//miner::find_nonce_for_given_block(bl, 1, 0);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins,
|
2015-09-18 11:55:31 +00:00
|
|
|
uint64_t fee, uint64_t& reward, int64_t& emissionChange) const {
|
2015-05-27 12:08:46 +00:00
|
|
|
assert(alreadyGeneratedCoins <= m_moneySupply);
|
|
|
|
assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t));
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor;
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-09-18 11:55:31 +00:00
|
|
|
medianSize = std::max(medianSize, m_blockGrantedFullRewardZone);
|
2015-05-27 12:08:46 +00:00
|
|
|
if (currentBlockSize > UINT64_C(2) * medianSize) {
|
|
|
|
logger(TRACE) << "Block cumulative size is too big: " << currentBlockSize << ", expected less than " << 2 * medianSize;
|
|
|
|
return false;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize);
|
2015-09-18 11:55:31 +00:00
|
|
|
uint64_t penalizedFee = getPenalizedAmount(fee, medianSize, currentBlockSize);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
emissionChange = penalizedBaseReward - (fee - penalizedFee);
|
|
|
|
reward = penalizedBaseReward + penalizedFee;
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
size_t Currency::maxBlockCumulativeSize(uint64_t height) const {
|
|
|
|
assert(height <= std::numeric_limits<uint64_t>::max() / m_maxBlockSizeGrowthSpeedNumerator);
|
|
|
|
size_t maxSize = static_cast<size_t>(m_maxBlockSizeInitial +
|
|
|
|
(height * m_maxBlockSizeGrowthSpeedNumerator) / m_maxBlockSizeGrowthSpeedDenominator);
|
|
|
|
assert(maxSize >= m_maxBlockSizeInitial);
|
|
|
|
return maxSize;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize,
|
|
|
|
uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx,
|
2015-09-18 11:55:31 +00:00
|
|
|
const BinaryArray& extraNonce/* = BinaryArray()*/, size_t maxOuts/* = 1*/) const {
|
2015-07-30 15:22:07 +00:00
|
|
|
tx.inputs.clear();
|
|
|
|
tx.outputs.clear();
|
2015-05-27 12:08:46 +00:00
|
|
|
tx.extra.clear();
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
KeyPair txkey = generateKeyPair();
|
|
|
|
addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey);
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!extraNonce.empty()) {
|
2015-07-30 15:22:07 +00:00
|
|
|
if (!addExtraNonceToTransactionExtra(tx.extra, extraNonce)) {
|
2015-05-27 12:08:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
BaseInput in;
|
|
|
|
in.blockIndex = height;
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
uint64_t blockReward;
|
|
|
|
int64_t emissionChange;
|
2015-09-18 11:55:31 +00:00
|
|
|
if (!getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, blockReward, emissionChange)) {
|
2015-05-27 12:08:46 +00:00
|
|
|
logger(INFO) << "Block is too big";
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
std::vector<uint64_t> outAmounts;
|
|
|
|
decompose_amount_into_digits(blockReward, m_defaultDustThreshold,
|
|
|
|
[&outAmounts](uint64_t a_chunk) { outAmounts.push_back(a_chunk); },
|
|
|
|
[&outAmounts](uint64_t a_dust) { outAmounts.push_back(a_dust); });
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!(1 <= maxOuts)) { logger(ERROR, BRIGHT_RED) << "max_out must be non-zero"; return false; }
|
|
|
|
while (maxOuts < outAmounts.size()) {
|
|
|
|
outAmounts[outAmounts.size() - 2] += outAmounts.back();
|
|
|
|
outAmounts.resize(outAmounts.size() - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t summaryAmounts = 0;
|
|
|
|
for (size_t no = 0; no < outAmounts.size(); no++) {
|
2015-07-30 15:22:07 +00:00
|
|
|
Crypto::KeyDerivation derivation = boost::value_initialized<Crypto::KeyDerivation>();
|
|
|
|
Crypto::PublicKey outEphemeralPubKey = boost::value_initialized<Crypto::PublicKey>();
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
bool r = Crypto::generate_key_derivation(minerAddress.viewPublicKey, txkey.secretKey, derivation);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!(r)) {
|
|
|
|
logger(ERROR, BRIGHT_RED)
|
|
|
|
<< "while creating outs: failed to generate_key_derivation("
|
2015-07-30 15:22:07 +00:00
|
|
|
<< minerAddress.viewPublicKey << ", " << txkey.secretKey << ")";
|
2015-05-27 12:08:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
r = Crypto::derive_public_key(derivation, no, minerAddress.spendPublicKey, outEphemeralPubKey);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!(r)) {
|
|
|
|
logger(ERROR, BRIGHT_RED)
|
|
|
|
<< "while creating outs: failed to derive_public_key("
|
|
|
|
<< derivation << ", " << no << ", "
|
2015-07-30 15:22:07 +00:00
|
|
|
<< minerAddress.spendPublicKey << ")";
|
2015-05-27 12:08:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
KeyOutput tk;
|
2015-05-27 12:08:46 +00:00
|
|
|
tk.key = outEphemeralPubKey;
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
TransactionOutput out;
|
|
|
|
summaryAmounts += out.amount = outAmounts[no];
|
|
|
|
out.target = tk;
|
2015-07-30 15:22:07 +00:00
|
|
|
tx.outputs.push_back(out);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!(summaryAmounts == blockReward)) {
|
|
|
|
logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, summaryAmounts = " << summaryAmounts << " not equal blockReward = " << blockReward;
|
|
|
|
return false;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
tx.version = CURRENT_TRANSACTION_VERSION;
|
|
|
|
//lock
|
|
|
|
tx.unlockTime = height + m_minedMoneyUnlockWindow;
|
2015-07-30 15:22:07 +00:00
|
|
|
tx.inputs.push_back(in);
|
2015-05-27 12:08:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
bool Currency::isFusionTransaction(const std::vector<uint64_t>& inputsAmounts, const std::vector<uint64_t>& outputsAmounts, size_t size) const {
|
2015-08-11 14:33:19 +00:00
|
|
|
if (size > fusionTxMaxSize()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
if (inputsAmounts.size() < fusionTxMinInputCount()) {
|
2015-08-11 14:33:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
if (inputsAmounts.size() < outputsAmounts.size() * fusionTxMinInOutCountRatio()) {
|
2015-08-11 14:33:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
uint64_t inputAmount = 0;
|
|
|
|
for (auto amount: inputsAmounts) {
|
|
|
|
if (amount < defaultDustThreshold()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inputAmount += amount;
|
|
|
|
}
|
|
|
|
|
2015-08-11 14:33:19 +00:00
|
|
|
std::vector<uint64_t> expectedOutputsAmounts;
|
2015-08-27 18:55:14 +00:00
|
|
|
expectedOutputsAmounts.reserve(outputsAmounts.size());
|
2015-08-11 14:33:19 +00:00
|
|
|
decomposeAmount(inputAmount, defaultDustThreshold(), expectedOutputsAmounts);
|
2015-08-27 18:55:14 +00:00
|
|
|
std::sort(expectedOutputsAmounts.begin(), expectedOutputsAmounts.end());
|
2015-08-11 14:33:19 +00:00
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
return expectedOutputsAmounts == outputsAmounts;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Currency::isFusionTransaction(const Transaction& transaction, size_t size) const {
|
|
|
|
assert(getObjectBinarySize(transaction) == size);
|
|
|
|
|
|
|
|
std::vector<uint64_t> outputsAmounts;
|
|
|
|
outputsAmounts.reserve(transaction.outputs.size());
|
|
|
|
for (const TransactionOutput& output : transaction.outputs) {
|
|
|
|
outputsAmounts.push_back(output.amount);
|
2015-08-11 14:33:19 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
return isFusionTransaction(getInputsAmounts(transaction), outputsAmounts, size);
|
|
|
|
}
|
2015-08-11 14:33:19 +00:00
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
bool Currency::isFusionTransaction(const Transaction& transaction) const {
|
|
|
|
return isFusionTransaction(transaction, getObjectBinarySize(transaction));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Currency::isAmountApplicableInFusionTransactionInput(uint64_t amount, uint64_t threshold) const {
|
|
|
|
uint8_t ignore;
|
|
|
|
return isAmountApplicableInFusionTransactionInput(amount, threshold, ignore);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Currency::isAmountApplicableInFusionTransactionInput(uint64_t amount, uint64_t threshold, uint8_t& amountPowerOfTen) const {
|
|
|
|
if (amount >= threshold) {
|
2015-08-11 14:33:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
if (amount < defaultDustThreshold()) {
|
|
|
|
return false;
|
2015-08-11 14:33:19 +00:00
|
|
|
}
|
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
auto it = std::lower_bound(PRETTY_AMOUNTS.begin(), PRETTY_AMOUNTS.end(), amount);
|
|
|
|
if (it == PRETTY_AMOUNTS.end() || amount != *it) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-08-11 14:33:19 +00:00
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
amountPowerOfTen = static_cast<uint8_t>(std::distance(PRETTY_AMOUNTS.begin(), it) / 9);
|
|
|
|
return true;
|
2015-08-11 14:33:19 +00:00
|
|
|
}
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
std::string Currency::accountAddressAsString(const AccountBase& account) const {
|
|
|
|
return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.getAccountKeys().address);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-07-15 12:23:00 +00:00
|
|
|
std::string Currency::accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const {
|
|
|
|
return getAccountAddressAsStr(m_publicAddressBase58Prefix, accountPublicAddress);
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const {
|
|
|
|
uint64_t prefix;
|
|
|
|
if (!CryptoNote::parseAccountAddressString(prefix, addr, str)) {
|
|
|
|
return false;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (prefix != m_publicAddressBase58Prefix) {
|
|
|
|
logger(DEBUGGING) << "Wrong address prefix: " << prefix << ", expected " << m_publicAddressBase58Prefix;
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
std::string Currency::formatAmount(uint64_t amount) const {
|
|
|
|
std::string s = std::to_string(amount);
|
|
|
|
if (s.size() < m_numberOfDecimalPlaces + 1) {
|
|
|
|
s.insert(0, m_numberOfDecimalPlaces + 1 - s.size(), '0');
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
s.insert(s.size() - m_numberOfDecimalPlaces, ".");
|
|
|
|
return s;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-08-11 14:33:19 +00:00
|
|
|
std::string Currency::formatAmount(int64_t amount) const {
|
|
|
|
std::string s = formatAmount(static_cast<uint64_t>(std::abs(amount)));
|
|
|
|
|
|
|
|
if (amount < 0) {
|
|
|
|
s.insert(0, "-");
|
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool Currency::parseAmount(const std::string& str, uint64_t& amount) const {
|
|
|
|
std::string strAmount = str;
|
|
|
|
boost::algorithm::trim(strAmount);
|
|
|
|
|
|
|
|
size_t pointIndex = strAmount.find_first_of('.');
|
|
|
|
size_t fractionSize;
|
|
|
|
if (std::string::npos != pointIndex) {
|
|
|
|
fractionSize = strAmount.size() - pointIndex - 1;
|
|
|
|
while (m_numberOfDecimalPlaces < fractionSize && '0' == strAmount.back()) {
|
|
|
|
strAmount.erase(strAmount.size() - 1, 1);
|
|
|
|
--fractionSize;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
if (m_numberOfDecimalPlaces < fractionSize) {
|
2014-08-13 10:51:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
strAmount.erase(pointIndex, 1);
|
|
|
|
} else {
|
|
|
|
fractionSize = 0;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (strAmount.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!std::all_of(strAmount.begin(), strAmount.end(), ::isdigit)) {
|
|
|
|
return false;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (fractionSize < m_numberOfDecimalPlaces) {
|
|
|
|
strAmount.append(m_numberOfDecimalPlaces - fractionSize, '0');
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return Common::fromString(strAmount, amount);
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
difficulty_type Currency::nextDifficulty(std::vector<uint64_t> timestamps,
|
|
|
|
std::vector<difficulty_type> cumulativeDifficulties) const {
|
|
|
|
assert(m_difficultyWindow >= 2);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (timestamps.size() > m_difficultyWindow) {
|
|
|
|
timestamps.resize(m_difficultyWindow);
|
|
|
|
cumulativeDifficulties.resize(m_difficultyWindow);
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
size_t length = timestamps.size();
|
|
|
|
assert(length == cumulativeDifficulties.size());
|
|
|
|
assert(length <= m_difficultyWindow);
|
|
|
|
if (length <= 1) {
|
|
|
|
return 1;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
sort(timestamps.begin(), timestamps.end());
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
size_t cutBegin, cutEnd;
|
|
|
|
assert(2 * m_difficultyCut <= m_difficultyWindow - 2);
|
|
|
|
if (length <= m_difficultyWindow - 2 * m_difficultyCut) {
|
|
|
|
cutBegin = 0;
|
|
|
|
cutEnd = length;
|
|
|
|
} else {
|
|
|
|
cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2;
|
|
|
|
cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut);
|
|
|
|
}
|
|
|
|
assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length);
|
|
|
|
uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin];
|
|
|
|
if (timeSpan == 0) {
|
|
|
|
timeSpan = 1;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin];
|
|
|
|
assert(totalWork > 0);
|
|
|
|
|
|
|
|
uint64_t low, high;
|
|
|
|
low = mul128(totalWork, m_difficultyTarget, &high);
|
|
|
|
if (high != 0 || low + timeSpan - 1 < low) {
|
|
|
|
return 0;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return (low + timeSpan - 1) / timeSpan;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-09-18 11:55:31 +00:00
|
|
|
bool Currency::checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic,
|
2015-07-30 15:22:07 +00:00
|
|
|
Crypto::Hash& proofOfWork) const {
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
if (!get_block_longhash(context, block, proofOfWork)) {
|
|
|
|
return false;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return check_hash(proofOfWork, currentDiffic);
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-08-27 18:55:14 +00:00
|
|
|
size_t Currency::getApproximateMaximumInputCount(size_t transactionSize, size_t outputCount, size_t mixinCount) const {
|
|
|
|
const size_t KEY_IMAGE_SIZE = sizeof(Crypto::KeyImage);
|
|
|
|
const size_t OUTPUT_KEY_SIZE = sizeof(decltype(KeyOutput::key));
|
|
|
|
const size_t AMOUNT_SIZE = sizeof(uint64_t) + 2; //varint
|
|
|
|
const size_t GLOBAL_INDEXES_VECTOR_SIZE_SIZE = sizeof(uint8_t);//varint
|
|
|
|
const size_t GLOBAL_INDEXES_INITIAL_VALUE_SIZE = sizeof(uint32_t);//varint
|
|
|
|
const size_t GLOBAL_INDEXES_DIFFERENCE_SIZE = sizeof(uint32_t);//varint
|
|
|
|
const size_t SIGNATURE_SIZE = sizeof(Crypto::Signature);
|
|
|
|
const size_t EXTRA_TAG_SIZE = sizeof(uint8_t);
|
|
|
|
const size_t INPUT_TAG_SIZE = sizeof(uint8_t);
|
|
|
|
const size_t OUTPUT_TAG_SIZE = sizeof(uint8_t);
|
|
|
|
const size_t PUBLIC_KEY_SIZE = sizeof(Crypto::PublicKey);
|
|
|
|
const size_t TRANSACTION_VERSION_SIZE = sizeof(uint8_t);
|
|
|
|
const size_t TRANSACTION_UNLOCK_TIME_SIZE = sizeof(uint64_t);
|
|
|
|
|
|
|
|
const size_t outputsSize = outputCount * (OUTPUT_TAG_SIZE + OUTPUT_KEY_SIZE + AMOUNT_SIZE);
|
|
|
|
const size_t headerSize = TRANSACTION_VERSION_SIZE + TRANSACTION_UNLOCK_TIME_SIZE + EXTRA_TAG_SIZE + PUBLIC_KEY_SIZE;
|
|
|
|
const size_t inputSize = INPUT_TAG_SIZE + AMOUNT_SIZE + KEY_IMAGE_SIZE + SIGNATURE_SIZE + GLOBAL_INDEXES_VECTOR_SIZE_SIZE + GLOBAL_INDEXES_INITIAL_VALUE_SIZE +
|
|
|
|
mixinCount * (GLOBAL_INDEXES_DIFFERENCE_SIZE + SIGNATURE_SIZE);
|
|
|
|
|
|
|
|
return (transactionSize - headerSize - outputsSize) / inputSize;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) {
|
|
|
|
maxBlockNumber(parameters::CRYPTONOTE_MAX_BLOCK_NUMBER);
|
|
|
|
maxBlockBlobSize(parameters::CRYPTONOTE_MAX_BLOCK_BLOB_SIZE);
|
|
|
|
maxTxSize(parameters::CRYPTONOTE_MAX_TX_SIZE);
|
|
|
|
publicAddressBase58Prefix(parameters::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
|
|
|
|
minedMoneyUnlockWindow(parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
timestampCheckWindow(parameters::BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW);
|
|
|
|
blockFutureTimeLimit(parameters::CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
moneySupply(parameters::MONEY_SUPPLY);
|
|
|
|
emissionSpeedFactor(parameters::EMISSION_SPEED_FACTOR);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
rewardBlocksWindow(parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
|
|
|
blockGrantedFullRewardZone(parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
|
|
|
|
minerTxBlobReservedSize(parameters::CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
numberOfDecimalPlaces(parameters::CRYPTONOTE_DISPLAY_DECIMAL_POINT);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
mininumFee(parameters::MINIMUM_FEE);
|
|
|
|
defaultDustThreshold(parameters::DEFAULT_DUST_THRESHOLD);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
difficultyTarget(parameters::DIFFICULTY_TARGET);
|
|
|
|
difficultyWindow(parameters::DIFFICULTY_WINDOW);
|
|
|
|
difficultyLag(parameters::DIFFICULTY_LAG);
|
|
|
|
difficultyCut(parameters::DIFFICULTY_CUT);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
maxBlockSizeInitial(parameters::MAX_BLOCK_SIZE_INITIAL);
|
|
|
|
maxBlockSizeGrowthSpeedNumerator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR);
|
|
|
|
maxBlockSizeGrowthSpeedDenominator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
lockedTxAllowedDeltaSeconds(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS);
|
|
|
|
lockedTxAllowedDeltaBlocks(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME);
|
|
|
|
mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME);
|
2015-07-15 12:23:00 +00:00
|
|
|
numberOfPeriodsToForgetTxDeletedFromPool(parameters::CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-08-11 14:33:19 +00:00
|
|
|
fusionTxMaxSize(parameters::FUSION_TX_MAX_SIZE);
|
|
|
|
fusionTxMinInputCount(parameters::FUSION_TX_MIN_INPUT_COUNT);
|
|
|
|
fusionTxMinInOutCountRatio(parameters::FUSION_TX_MIN_IN_OUT_COUNT_RATIO);
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
blocksFileName(parameters::CRYPTONOTE_BLOCKS_FILENAME);
|
|
|
|
blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME);
|
|
|
|
blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME);
|
|
|
|
txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME);
|
2015-07-30 15:22:07 +00:00
|
|
|
blockchinIndicesFileName(parameters::CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME);
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
testnet(false);
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-09-18 11:55:31 +00:00
|
|
|
Transaction CurrencyBuilder::generateGenesisTransaction() {
|
|
|
|
CryptoNote::Transaction tx;
|
|
|
|
CryptoNote::AccountPublicAddress ac = boost::value_initialized<CryptoNote::AccountPublicAddress>();
|
|
|
|
m_currency.constructMinerTx(0, 0, 0, 0, 0, ac, tx); // zero fee in genesis
|
|
|
|
|
|
|
|
return tx;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CurrencyBuilder& CurrencyBuilder::emissionSpeedFactor(unsigned int val) {
|
|
|
|
if (val <= 0 || val > 8 * sizeof(uint64_t)) {
|
|
|
|
throw std::invalid_argument("val at emissionSpeedFactor()");
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
m_currency.m_emissionSpeedFactor = val;
|
|
|
|
return *this;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
CurrencyBuilder& CurrencyBuilder::numberOfDecimalPlaces(size_t val) {
|
|
|
|
m_currency.m_numberOfDecimalPlaces = val;
|
|
|
|
m_currency.m_coin = 1;
|
|
|
|
for (size_t i = 0; i < m_currency.m_numberOfDecimalPlaces; ++i) {
|
|
|
|
m_currency.m_coin *= 10;
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
CurrencyBuilder& CurrencyBuilder::difficultyWindow(size_t val) {
|
|
|
|
if (val < 2) {
|
|
|
|
throw std::invalid_argument("val at difficultyWindow()");
|
2014-08-13 10:51:37 +00:00
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
m_currency.m_difficultyWindow = val;
|
|
|
|
return *this;
|
|
|
|
}
|
2014-08-13 10:51:37 +00:00
|
|
|
|
|
|
|
}
|