danicoin/tests/TestGenerator/TestGenerator.cpp

378 lines
16 KiB
C++
Raw Normal View History

2015-05-27 12:08:46 +00:00
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
2014-08-13 10:51:37 +00:00
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "TestGenerator.h"
2015-07-15 12:23:00 +00:00
#include <Common/Math.h>
2015-07-30 15:22:07 +00:00
#include "CryptoNoteCore/Account.h"
#include "CryptoNoteCore/Miner.h"
#include "CryptoNoteCore/TransactionExtra.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
2014-08-13 10:51:37 +00:00
using namespace std;
2015-05-27 12:08:46 +00:00
using namespace CryptoNote;
2014-08-13 10:51:37 +00:00
2015-07-15 12:23:00 +00:00
#ifndef CHECK_AND_ASSERT_MES
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {std::cerr << message << std::endl; return fail_ret_val;};}while(0)
#endif
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
void test_generator::getBlockchain(std::vector<BlockInfo>& blockchain, const Crypto::Hash& head, size_t n) const {
Crypto::Hash curr = head;
while (curr != NULL_HASH && blockchain.size() < n) {
2014-08-13 10:51:37 +00:00
auto it = m_blocksInfo.find(curr);
if (m_blocksInfo.end() == it) {
throw std::runtime_error("block hash wasn't found");
}
blockchain.push_back(it->second);
2015-07-30 15:22:07 +00:00
curr = it->second.previousBlockHash;
2014-08-13 10:51:37 +00:00
}
std::reverse(blockchain.begin(), blockchain.end());
}
2015-07-30 15:22:07 +00:00
void test_generator::getLastNBlockSizes(std::vector<size_t>& blockSizes, const Crypto::Hash& head, size_t n) const {
2014-08-13 10:51:37 +00:00
std::vector<BlockInfo> blockchain;
getBlockchain(blockchain, head, n);
for (auto& bi : blockchain) {
blockSizes.push_back(bi.blockSize);
}
}
2015-07-30 15:22:07 +00:00
uint64_t test_generator::getAlreadyGeneratedCoins(const Crypto::Hash& blockId) const {
2014-08-13 10:51:37 +00:00
auto it = m_blocksInfo.find(blockId);
if (it == m_blocksInfo.end()) {
throw std::runtime_error("block hash wasn't found");
}
return it->second.alreadyGeneratedCoins;
}
2015-05-27 12:08:46 +00:00
uint64_t test_generator::getAlreadyGeneratedCoins(const CryptoNote::Block& blk) const {
2015-07-30 15:22:07 +00:00
Crypto::Hash blkHash;
2014-08-13 10:51:37 +00:00
get_block_hash(blk, blkHash);
return getAlreadyGeneratedCoins(blkHash);
}
2015-05-27 12:08:46 +00:00
void test_generator::addBlock(const CryptoNote::Block& blk, size_t tsxSize, uint64_t fee,
2014-08-13 10:51:37 +00:00
std::vector<size_t>& blockSizes, uint64_t alreadyGeneratedCoins) {
2015-07-30 15:22:07 +00:00
const size_t blockSize = tsxSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
int64_t emissionChange;
uint64_t blockReward;
bool penalizeFee = blk.majorVersion > BLOCK_MAJOR_VERSION_1;
2015-07-15 12:23:00 +00:00
m_currency.getBlockReward(Common::medianValue(blockSizes), blockSize, alreadyGeneratedCoins, fee, penalizeFee,
2014-08-13 10:51:37 +00:00
blockReward, emissionChange);
2015-07-30 15:22:07 +00:00
m_blocksInfo[get_block_hash(blk)] = BlockInfo(blk.previousBlockHash, alreadyGeneratedCoins + emissionChange, blockSize);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool test_generator::constructBlock(CryptoNote::Block& blk, uint32_t height, const Crypto::Hash& previousBlockHash,
const CryptoNote::AccountBase& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins,
2015-05-27 12:08:46 +00:00
std::vector<size_t>& blockSizes, const std::list<CryptoNote::Transaction>& txList) {
2014-08-13 10:51:37 +00:00
blk.majorVersion = defaultMajorVersion;
blk.minorVersion = defaultMinorVersion;
blk.timestamp = timestamp;
2015-07-30 15:22:07 +00:00
blk.previousBlockHash = previousBlockHash;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
blk.transactionHashes.reserve(txList.size());
2014-08-13 10:51:37 +00:00
for (const Transaction &tx : txList) {
2015-07-30 15:22:07 +00:00
Crypto::Hash tx_hash;
getObjectHash(tx, tx_hash);
blk.transactionHashes.push_back(tx_hash);
2014-08-13 10:51:37 +00:00
}
uint64_t totalFee = 0;
size_t txsSize = 0;
for (auto& tx : txList) {
uint64_t fee = 0;
bool r = get_tx_fee(tx, fee);
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
totalFee += fee;
2015-07-30 15:22:07 +00:00
txsSize += getObjectBinarySize(tx);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
blk.baseTransaction = boost::value_initialized<Transaction>();
size_t targetBlockSize = txsSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
while (true) {
2015-07-15 12:23:00 +00:00
if (!m_currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, targetBlockSize,
2015-07-30 15:22:07 +00:00
totalFee, minerAcc.getAccountKeys().address, blk.baseTransaction, BinaryArray(), 10)) {
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
size_t actualBlockSize = txsSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
if (targetBlockSize < actualBlockSize) {
targetBlockSize = actualBlockSize;
} else if (actualBlockSize < targetBlockSize) {
size_t delta = targetBlockSize - actualBlockSize;
2015-07-30 15:22:07 +00:00
blk.baseTransaction.extra.resize(blk.baseTransaction.extra.size() + delta, 0);
actualBlockSize = txsSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
if (actualBlockSize == targetBlockSize) {
break;
} else {
CHECK_AND_ASSERT_MES(targetBlockSize < actualBlockSize, false, "Unexpected block size");
delta = actualBlockSize - targetBlockSize;
2015-07-30 15:22:07 +00:00
blk.baseTransaction.extra.resize(blk.baseTransaction.extra.size() - delta);
actualBlockSize = txsSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
if (actualBlockSize == targetBlockSize) {
break;
} else {
CHECK_AND_ASSERT_MES(actualBlockSize < targetBlockSize, false, "Unexpected block size");
2015-07-30 15:22:07 +00:00
blk.baseTransaction.extra.resize(blk.baseTransaction.extra.size() + delta, 0);
targetBlockSize = txsSize + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
}
}
} else {
break;
}
}
if (blk.majorVersion >= BLOCK_MAJOR_VERSION_2) {
blk.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1;
blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0;
2015-07-30 15:22:07 +00:00
blk.parentBlock.transactionCount = 1;
blk.parentBlock.baseTransaction.version = 0;
blk.parentBlock.baseTransaction.unlockTime = 0;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
CryptoNote::TransactionExtraMergeMiningTag mmTag;
2014-08-13 10:51:37 +00:00
mmTag.depth = 0;
2015-07-30 15:22:07 +00:00
if (!CryptoNote::get_aux_block_header_hash(blk, mmTag.merkleRoot)) {
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
blk.parentBlock.baseTransaction.extra.clear();
if (!CryptoNote::appendMergeMiningTagToExtra(blk.parentBlock.baseTransaction.extra, mmTag)) {
2014-08-13 10:51:37 +00:00
return false;
}
}
// Nonce search...
blk.nonce = 0;
2015-07-30 15:22:07 +00:00
Crypto::cn_context context;
2014-08-13 10:51:37 +00:00
while (!miner::find_nonce_for_given_block(context, blk, getTestDifficulty())) {
blk.timestamp++;
}
addBlock(blk, txsSize, totalFee, blockSizes, alreadyGeneratedCoins);
return true;
}
2015-07-30 15:22:07 +00:00
bool test_generator::constructBlock(CryptoNote::Block& blk, const CryptoNote::AccountBase& minerAcc, uint64_t timestamp) {
2014-08-13 10:51:37 +00:00
std::vector<size_t> blockSizes;
2015-05-27 12:08:46 +00:00
std::list<CryptoNote::Transaction> txList;
2015-07-30 15:22:07 +00:00
return constructBlock(blk, 0, NULL_HASH, minerAcc, timestamp, 0, blockSizes, txList);
2014-08-13 10:51:37 +00:00
}
2015-05-27 12:08:46 +00:00
bool test_generator::constructBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev,
2015-07-30 15:22:07 +00:00
const CryptoNote::AccountBase& minerAcc,
2015-05-27 12:08:46 +00:00
const std::list<CryptoNote::Transaction>& txList/* = std::list<CryptoNote::Transaction>()*/) {
2015-07-30 15:22:07 +00:00
uint32_t height = boost::get<BaseInput>(blkPrev.baseTransaction.inputs.front()).blockIndex + 1;
Crypto::Hash previousBlockHash = get_block_hash(blkPrev);
2014-08-13 10:51:37 +00:00
// Keep difficulty unchanged
uint64_t timestamp = blkPrev.timestamp + m_currency.difficultyTarget();
2015-07-30 15:22:07 +00:00
uint64_t alreadyGeneratedCoins = getAlreadyGeneratedCoins(previousBlockHash);
2014-08-13 10:51:37 +00:00
std::vector<size_t> blockSizes;
2015-07-30 15:22:07 +00:00
getLastNBlockSizes(blockSizes, previousBlockHash, m_currency.rewardBlocksWindow());
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
return constructBlock(blk, height, previousBlockHash, minerAcc, timestamp, alreadyGeneratedCoins, blockSizes, txList);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, const AccountBase& minerAcc,
2014-08-13 10:51:37 +00:00
int actualParams/* = bf_none*/, uint8_t majorVer/* = 0*/,
uint8_t minorVer/* = 0*/, uint64_t timestamp/* = 0*/,
2015-07-30 15:22:07 +00:00
const Crypto::Hash& previousBlockHash/* = Crypto::Hash()*/, const difficulty_type& diffic/* = 1*/,
const Transaction& baseTransaction/* = transaction()*/,
const std::vector<Crypto::Hash>& transactionHashes/* = std::vector<Crypto::Hash>()*/,
2014-08-13 10:51:37 +00:00
size_t txsSizes/* = 0*/, uint64_t fee/* = 0*/) {
blk.majorVersion = actualParams & bf_major_ver ? majorVer : defaultMajorVersion;
blk.minorVersion = actualParams & bf_minor_ver ? minorVer : defaultMinorVersion;
blk.timestamp = actualParams & bf_timestamp ? timestamp : prevBlock.timestamp + m_currency.difficultyTarget(); // Keep difficulty unchanged
2015-07-30 15:22:07 +00:00
blk.previousBlockHash = actualParams & bf_prev_id ? previousBlockHash : get_block_hash(prevBlock);
blk.transactionHashes = actualParams & bf_tx_hashes ? transactionHashes : std::vector<Crypto::Hash>();
blk.parentBlock.baseTransaction.version = 0;
blk.parentBlock.baseTransaction.unlockTime = 0;
2014-08-13 10:51:37 +00:00
2015-05-27 12:08:46 +00:00
uint32_t height = get_block_height(prevBlock) + 1;
2014-08-13 10:51:37 +00:00
uint64_t alreadyGeneratedCoins = getAlreadyGeneratedCoins(prevBlock);
std::vector<size_t> blockSizes;
getLastNBlockSizes(blockSizes, get_block_hash(prevBlock), m_currency.rewardBlocksWindow());
if (actualParams & bf_miner_tx) {
2015-07-30 15:22:07 +00:00
blk.baseTransaction = baseTransaction;
2014-08-13 10:51:37 +00:00
} else {
2015-07-30 15:22:07 +00:00
blk.baseTransaction = boost::value_initialized<Transaction>();
size_t currentBlockSize = txsSizes + getObjectBinarySize(blk.baseTransaction);
2014-08-13 10:51:37 +00:00
// TODO: This will work, until size of constructed block is less then m_currency.blockGrantedFullRewardZone()
2015-07-15 12:23:00 +00:00
if (!m_currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, currentBlockSize, 0,
2015-07-30 15:22:07 +00:00
minerAcc.getAccountKeys().address, blk.baseTransaction, BinaryArray(), 1, blk.majorVersion > BLOCK_MAJOR_VERSION_1)) {
2014-08-13 10:51:37 +00:00
return false;
}
}
if (blk.majorVersion >= BLOCK_MAJOR_VERSION_2) {
blk.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1;
blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0;
2015-07-30 15:22:07 +00:00
blk.parentBlock.transactionCount = 1;
2014-08-13 10:51:37 +00:00
2015-07-30 15:22:07 +00:00
CryptoNote::TransactionExtraMergeMiningTag mmTag;
2014-08-13 10:51:37 +00:00
mmTag.depth = 0;
2015-07-30 15:22:07 +00:00
if (!CryptoNote::get_aux_block_header_hash(blk, mmTag.merkleRoot)) {
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
blk.parentBlock.baseTransaction.extra.clear();
if (!CryptoNote::appendMergeMiningTagToExtra(blk.parentBlock.baseTransaction.extra, mmTag)) {
2014-08-13 10:51:37 +00:00
return false;
}
}
difficulty_type aDiffic = actualParams & bf_diffic ? diffic : getTestDifficulty();
if (1 < aDiffic) {
fillNonce(blk, aDiffic);
}
addBlock(blk, txsSizes, fee, blockSizes, alreadyGeneratedCoins);
return true;
}
2015-05-27 12:08:46 +00:00
bool test_generator::constructBlockManuallyTx(CryptoNote::Block& blk, const CryptoNote::Block& prevBlock,
2015-07-30 15:22:07 +00:00
const CryptoNote::AccountBase& minerAcc,
const std::vector<Crypto::Hash>& transactionHashes, size_t txsSize) {
return constructBlockManually(blk, prevBlock, minerAcc, bf_tx_hashes, 0, 0, 0, Crypto::Hash(), 0, Transaction(),
transactionHashes, txsSize);
2014-08-13 10:51:37 +00:00
}
2015-05-27 12:08:46 +00:00
bool test_generator::constructMaxSizeBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev,
2015-07-30 15:22:07 +00:00
const CryptoNote::AccountBase& minerAccount,
2014-08-13 10:51:37 +00:00
size_t medianBlockCount/* = 0*/,
2015-05-27 12:08:46 +00:00
const std::list<CryptoNote::Transaction>& txList/* = std::list<CryptoNote::Transaction>()*/) {
2014-08-13 10:51:37 +00:00
std::vector<size_t> blockSizes;
medianBlockCount = medianBlockCount == 0 ? m_currency.rewardBlocksWindow() : medianBlockCount;
getLastNBlockSizes(blockSizes, get_block_hash(blkPrev), medianBlockCount);
2015-07-15 12:23:00 +00:00
size_t median = Common::medianValue(blockSizes);
2014-08-14 15:41:44 +00:00
size_t blockGrantedFullRewardZone = defaultMajorVersion <= BLOCK_MAJOR_VERSION_1 ?
2015-05-27 12:08:46 +00:00
CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 :
2014-08-14 15:41:44 +00:00
m_currency.blockGrantedFullRewardZone();
median = std::max(median, blockGrantedFullRewardZone);
2014-08-13 10:51:37 +00:00
uint64_t totalFee = 0;
size_t txsSize = 0;
2015-07-30 15:22:07 +00:00
std::vector<Crypto::Hash> transactionHashes;
2014-08-13 10:51:37 +00:00
for (auto& tx : txList) {
uint64_t fee = 0;
bool r = get_tx_fee(tx, fee);
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_max_size_block");
totalFee += fee;
2015-07-30 15:22:07 +00:00
txsSize += getObjectBinarySize(tx);
transactionHashes.push_back(getObjectHash(tx));
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
Transaction baseTransaction;
bool r = constructMinerTxBySize(m_currency, baseTransaction, get_block_height(blkPrev) + 1,
getAlreadyGeneratedCoins(blkPrev), minerAccount.getAccountKeys().address, blockSizes,
2014-08-13 10:51:37 +00:00
2 * median - txsSize, 2 * median, totalFee, defaultMajorVersion > BLOCK_MAJOR_VERSION_1);
if (!r) {
return false;
}
return constructBlockManually(blk, blkPrev, minerAccount, test_generator::bf_miner_tx | test_generator::bf_tx_hashes,
2015-07-30 15:22:07 +00:00
0, 0, 0, Crypto::Hash(), 0, baseTransaction, transactionHashes, txsSize, totalFee);
2014-08-13 10:51:37 +00:00
}
2015-05-27 12:08:46 +00:00
void fillNonce(CryptoNote::Block& blk, const difficulty_type& diffic) {
2014-08-13 10:51:37 +00:00
blk.nonce = 0;
2015-07-30 15:22:07 +00:00
Crypto::cn_context context;
2014-08-13 10:51:37 +00:00
while (!miner::find_nonce_for_given_block(context, blk, diffic)) {
blk.timestamp++;
}
}
2015-05-27 12:08:46 +00:00
bool constructMinerTxManually(const CryptoNote::Currency& currency, uint32_t height, uint64_t alreadyGeneratedCoins,
2014-08-13 10:51:37 +00:00
const AccountPublicAddress& minerAddress, Transaction& tx, uint64_t fee,
KeyPair* pTxKey/* = 0*/) {
2015-07-30 15:22:07 +00:00
KeyPair txkey = generateKeyPair();
addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey);
2014-08-13 10:51:37 +00:00
if (0 != pTxKey) {
*pTxKey = txkey;
}
2015-07-30 15:22:07 +00:00
BaseInput in;
in.blockIndex = height;
tx.inputs.push_back(in);
2014-08-13 10:51:37 +00:00
// This will work, until size of constructed block is less then currency.blockGrantedFullRewardZone()
int64_t emissionChange;
uint64_t blockReward;
if (!currency.getBlockReward(0, 0, alreadyGeneratedCoins, fee, false, blockReward, emissionChange)) {
2015-07-15 12:23:00 +00:00
std::cerr << "Block is too big" << std::endl;
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
Crypto::KeyDerivation derivation;
Crypto::PublicKey outEphPublicKey;
Crypto::generate_key_derivation(minerAddress.viewPublicKey, txkey.secretKey, derivation);
Crypto::derive_public_key(derivation, 0, minerAddress.spendPublicKey, outEphPublicKey);
2014-08-13 10:51:37 +00:00
TransactionOutput out;
out.amount = blockReward;
2015-07-30 15:22:07 +00:00
out.target = KeyOutput{outEphPublicKey};
tx.outputs.push_back(out);
2014-08-13 10:51:37 +00:00
tx.version = CURRENT_TRANSACTION_VERSION;
tx.unlockTime = height + currency.minedMoneyUnlockWindow();
return true;
}
2015-07-30 15:22:07 +00:00
bool constructMinerTxBySize(const CryptoNote::Currency& currency, CryptoNote::Transaction& baseTransaction, uint32_t height,
2015-05-27 12:08:46 +00:00
uint64_t alreadyGeneratedCoins, const CryptoNote::AccountPublicAddress& minerAddress,
2014-08-13 10:51:37 +00:00
std::vector<size_t>& blockSizes, size_t targetTxSize, size_t targetBlockSize,
uint64_t fee/* = 0*/, bool penalizeFee/* = false*/) {
2015-07-15 12:23:00 +00:00
if (!currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, targetBlockSize,
2015-07-30 15:22:07 +00:00
fee, minerAddress, baseTransaction, CryptoNote::BinaryArray(), 1, penalizeFee)) {
2014-08-13 10:51:37 +00:00
return false;
}
2015-07-30 15:22:07 +00:00
size_t currentSize = getObjectBinarySize(baseTransaction);
2014-08-13 10:51:37 +00:00
size_t tryCount = 0;
while (targetTxSize != currentSize) {
++tryCount;
if (10 < tryCount) {
return false;
}
if (targetTxSize < currentSize) {
size_t diff = currentSize - targetTxSize;
2015-07-30 15:22:07 +00:00
if (diff <= baseTransaction.extra.size()) {
baseTransaction.extra.resize(baseTransaction.extra.size() - diff);
2014-08-13 10:51:37 +00:00
} else {
return false;
}
} else {
size_t diff = targetTxSize - currentSize;
2015-07-30 15:22:07 +00:00
baseTransaction.extra.resize(baseTransaction.extra.size() + diff);
2014-08-13 10:51:37 +00:00
}
2015-07-30 15:22:07 +00:00
currentSize = getObjectBinarySize(baseTransaction);
2014-08-13 10:51:37 +00:00
}
return true;
}