Implement transaction expiry for Overwinter
This commit is contained in:
parent
c5904fb2a4
commit
9bb37bf0d5
|
@ -164,6 +164,15 @@ static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
|
||||||
tx.nVersion = (int) newVersion;
|
tx.nVersion = (int) newVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void MutateTxExpiry(CMutableTransaction& tx, const string& cmdVal)
|
||||||
|
{
|
||||||
|
int64_t newExpiry = atoi64(cmdVal);
|
||||||
|
if (newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) {
|
||||||
|
throw runtime_error("Invalid TX expiry requested");
|
||||||
|
}
|
||||||
|
tx.nExpiryHeight = (int) newExpiry;
|
||||||
|
}
|
||||||
|
|
||||||
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
|
static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
|
||||||
{
|
{
|
||||||
int64_t newLocktime = atoi64(cmdVal);
|
int64_t newLocktime = atoi64(cmdVal);
|
||||||
|
@ -503,6 +512,8 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
|
||||||
MutateTxVersion(tx, commandVal);
|
MutateTxVersion(tx, commandVal);
|
||||||
else if (command == "locktime")
|
else if (command == "locktime")
|
||||||
MutateTxLocktime(tx, commandVal);
|
MutateTxLocktime(tx, commandVal);
|
||||||
|
else if (command == "expiry")
|
||||||
|
MutateTxExpiry(tx, commandVal);
|
||||||
|
|
||||||
else if (command == "delin")
|
else if (command == "delin")
|
||||||
MutateTxDelInput(tx, commandVal);
|
MutateTxDelInput(tx, commandVal);
|
||||||
|
|
|
@ -22,6 +22,8 @@ static const unsigned int MAX_BLOCK_SIGOPS = 20000;
|
||||||
static const unsigned int MAX_TX_SIZE = 100000;
|
static const unsigned int MAX_TX_SIZE = 100000;
|
||||||
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
|
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
|
||||||
static const int COINBASE_MATURITY = 100;
|
static const int COINBASE_MATURITY = 100;
|
||||||
|
/** The minimum value which is invalid for expiry height, used by CTransaction and CMutableTransaction */
|
||||||
|
static constexpr uint32_t TX_EXPIRY_HEIGHT_THRESHOLD = 500000000;
|
||||||
|
|
||||||
/** Flags for LockTime() */
|
/** Flags for LockTime() */
|
||||||
enum {
|
enum {
|
||||||
|
|
19
src/main.cpp
19
src/main.cpp
|
@ -73,6 +73,8 @@ size_t nCoinCacheUsage = 5000 * 300;
|
||||||
uint64_t nPruneTarget = 0;
|
uint64_t nPruneTarget = 0;
|
||||||
bool fAlerts = DEFAULT_ALERTS;
|
bool fAlerts = DEFAULT_ALERTS;
|
||||||
|
|
||||||
|
unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA;
|
||||||
|
|
||||||
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
|
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
|
||||||
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||||
|
|
||||||
|
@ -718,6 +720,14 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsExpiredTx(const CTransaction &tx, int nBlockHeight)
|
||||||
|
{
|
||||||
|
if (tx.nExpiryHeight == 0 || tx.IsCoinBase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return static_cast<uint32_t>(nBlockHeight) > tx.nExpiryHeight;
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckFinalTx(const CTransaction &tx, int flags)
|
bool CheckFinalTx(const CTransaction &tx, int flags)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
|
@ -884,6 +894,11 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||||
return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"),
|
return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"),
|
||||||
REJECT_INVALID, "tx-overwinter-active");
|
REJECT_INVALID, "tx-overwinter-active");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that all transactions are unexpired
|
||||||
|
if (IsExpiredTx(tx, nHeight)) {
|
||||||
|
return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
||||||
|
@ -2659,6 +2674,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
|
||||||
// Remove conflicting transactions from the mempool.
|
// Remove conflicting transactions from the mempool.
|
||||||
list<CTransaction> txConflicted;
|
list<CTransaction> txConflicted;
|
||||||
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
|
mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload());
|
||||||
|
|
||||||
|
// Remove transactions that expire at new block height from mempool
|
||||||
|
mempool.removeExpired(pindexNew->nHeight);
|
||||||
|
|
||||||
// Update chainActive & related variables.
|
// Update chainActive & related variables.
|
||||||
UpdateTip(pindexNew);
|
UpdateTip(pindexNew);
|
||||||
// Tell wallet about transactions that went from mempool
|
// Tell wallet about transactions that went from mempool
|
||||||
|
|
|
@ -68,6 +68,8 @@ static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
|
||||||
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100;
|
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100;
|
||||||
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
|
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
|
||||||
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
|
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
|
||||||
|
/** Default for -txexpirydelta, in number of blocks */
|
||||||
|
static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 20;
|
||||||
/** The maximum size of a blk?????.dat file (since 0.8) */
|
/** The maximum size of a blk?????.dat file (since 0.8) */
|
||||||
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
||||||
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
|
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
|
||||||
|
@ -110,6 +112,7 @@ struct BlockHasher
|
||||||
size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
|
size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern unsigned int expiryDelta;
|
||||||
extern CScript COINBASE_FLAGS;
|
extern CScript COINBASE_FLAGS;
|
||||||
extern CCriticalSection cs_main;
|
extern CCriticalSection cs_main;
|
||||||
extern CTxMemPool mempool;
|
extern CTxMemPool mempool;
|
||||||
|
@ -371,6 +374,12 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
|
||||||
*/
|
*/
|
||||||
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
|
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if transaction is expired and can be included in a block with the
|
||||||
|
* specified height. Consensus critical.
|
||||||
|
*/
|
||||||
|
bool IsExpiredTx(const CTransaction &tx, int nBlockHeight);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if transaction will be final in the next block to be created.
|
* Check if transaction will be final in the next block to be created.
|
||||||
*
|
*
|
||||||
|
|
|
@ -166,7 +166,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
? nMedianTimePast
|
? nMedianTimePast
|
||||||
: pblock->GetBlockTime();
|
: pblock->GetBlockTime();
|
||||||
|
|
||||||
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff))
|
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
COrphan* porphan = NULL;
|
COrphan* porphan = NULL;
|
||||||
|
@ -345,6 +345,8 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||||
txNew.vout.resize(1);
|
txNew.vout.resize(1);
|
||||||
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
|
txNew.vout[0].scriptPubKey = scriptPubKeyIn;
|
||||||
txNew.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
txNew.vout[0].nValue = GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
||||||
|
// Set to 0 so expiry height does not apply to coinbase txs
|
||||||
|
txNew.nExpiryHeight = 0;
|
||||||
|
|
||||||
if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight())) {
|
if ((nHeight > 0) && (nHeight <= chainparams.GetConsensus().GetLastFoundersRewardBlockHeight())) {
|
||||||
// Founders reward is 20% of the block subsidy
|
// Founders reward is 20% of the block subsidy
|
||||||
|
|
|
@ -304,9 +304,6 @@ public:
|
||||||
std::string ToString() const;
|
std::string ToString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The maximum value which is valid for expiry height, used by CTransaction and CMutableTransaction
|
|
||||||
static constexpr uint32_t TX_EXPIRY_HEIGHT_THRESHOLD = 500000000;
|
|
||||||
|
|
||||||
// Overwinter version group id
|
// Overwinter version group id
|
||||||
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
|
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
|
||||||
static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||||
|
|
|
@ -198,6 +198,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp)
|
||||||
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
|
" \"txid\" : \"id\", (string) The transaction id (same as provided)\n"
|
||||||
" \"version\" : n, (numeric) The version\n"
|
" \"version\" : n, (numeric) The version\n"
|
||||||
" \"locktime\" : ttt, (numeric) The lock time\n"
|
" \"locktime\" : ttt, (numeric) The lock time\n"
|
||||||
|
" \"expiryheight\" : ttt, (numeric, optional) The block height after which the transaction expires\n"
|
||||||
" \"vin\" : [ (array of json objects)\n"
|
" \"vin\" : [ (array of json objects)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" \"txid\": \"id\", (string) The transaction id\n"
|
" \"txid\": \"id\", (string) The transaction id\n"
|
||||||
|
@ -443,8 +444,16 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
|
||||||
UniValue inputs = params[0].get_array();
|
UniValue inputs = params[0].get_array();
|
||||||
UniValue sendTo = params[1].get_obj();
|
UniValue sendTo = params[1].get_obj();
|
||||||
|
|
||||||
|
int nextBlockHeight = chainActive.Height() + 1;
|
||||||
CMutableTransaction rawTx = CreateNewContextualCMutableTransaction(
|
CMutableTransaction rawTx = CreateNewContextualCMutableTransaction(
|
||||||
Params().GetConsensus(), chainActive.Height() + 1);
|
Params().GetConsensus(), nextBlockHeight);
|
||||||
|
|
||||||
|
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
|
||||||
|
rawTx.nExpiryHeight = nextBlockHeight + expiryDelta;
|
||||||
|
if (rawTx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t idx = 0; idx < inputs.size(); idx++) {
|
for (size_t idx = 0; idx < inputs.size(); idx++) {
|
||||||
const UniValue& input = inputs[idx];
|
const UniValue& input = inputs[idx];
|
||||||
|
@ -497,7 +506,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp)
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" \"txid\" : \"id\", (string) The transaction id\n"
|
" \"txid\" : \"id\", (string) The transaction id\n"
|
||||||
" \"overwintered\" : bool (boolean) The Overwintered flag\n"
|
" \"overwintered\" : bool (boolean) The Overwintered flag\n"
|
||||||
" \"version\" : n, (numeric) The version\n"
|
" \"version\" : n, (numeric) The version\n"
|
||||||
" \"versiongroupid\": \"hex\" (string, optional) The version group id (Overwintered txs)\n"
|
" \"versiongroupid\": \"hex\" (string, optional) The version group id (Overwintered txs)\n"
|
||||||
" \"locktime\" : ttt, (numeric) The lock time\n"
|
" \"locktime\" : ttt, (numeric) The lock time\n"
|
||||||
|
|
|
@ -256,6 +256,24 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
||||||
|
{
|
||||||
|
// Remove expired txs from the mempool
|
||||||
|
LOCK(cs);
|
||||||
|
list<CTransaction> transactionsToRemove;
|
||||||
|
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++)
|
||||||
|
{
|
||||||
|
const CTransaction& tx = it->GetTx();
|
||||||
|
if (IsExpiredTx(tx, nBlockHeight)) {
|
||||||
|
transactionsToRemove.push_back(tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const CTransaction& tx : transactionsToRemove) {
|
||||||
|
list<CTransaction> removed;
|
||||||
|
remove(tx, removed, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
|
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -168,6 +168,7 @@ public:
|
||||||
void removeWithAnchor(const uint256 &invalidRoot);
|
void removeWithAnchor(const uint256 &invalidRoot);
|
||||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||||
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
||||||
|
void removeExpired(unsigned int nBlockHeight);
|
||||||
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||||
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
std::list<CTransaction>& conflicts, bool fCurrentEstimate = true);
|
||||||
void removeWithoutBranchId(uint32_t nMemPoolBranchId);
|
void removeWithoutBranchId(uint32_t nMemPoolBranchId);
|
||||||
|
|
|
@ -86,6 +86,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
|
||||||
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
|
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
|
||||||
entry.push_back(Pair("blockindex", wtx.nIndex));
|
entry.push_back(Pair("blockindex", wtx.nIndex));
|
||||||
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
|
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
|
||||||
|
entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight));
|
||||||
}
|
}
|
||||||
uint256 hash = wtx.GetHash();
|
uint256 hash = wtx.GetHash();
|
||||||
entry.push_back(Pair("txid", hash.GetHex()));
|
entry.push_back(Pair("txid", hash.GetHex()));
|
||||||
|
@ -3534,11 +3535,15 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
||||||
UniValue contextInfo = o;
|
UniValue contextInfo = o;
|
||||||
|
|
||||||
// Contextual transaction we will build on
|
// Contextual transaction we will build on
|
||||||
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), chainActive.Height() + 1);
|
int nextBlockHeight = chainActive.Height() + 1;
|
||||||
|
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight);
|
||||||
bool isShielded = !fromTaddr || zaddrRecipients.size() > 0;
|
bool isShielded = !fromTaddr || zaddrRecipients.size() > 0;
|
||||||
if (contextualTx.nVersion == 1 && isShielded) {
|
if (contextualTx.nVersion == 1 && isShielded) {
|
||||||
contextualTx.nVersion = 2; // Tx format should support vjoinsplits
|
contextualTx.nVersion = 2; // Tx format should support vjoinsplits
|
||||||
}
|
}
|
||||||
|
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
|
||||||
|
contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta;
|
||||||
|
}
|
||||||
|
|
||||||
// Create operation and add to global queue
|
// Create operation and add to global queue
|
||||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||||
|
@ -3725,12 +3730,15 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
|
||||||
contextInfo.push_back(Pair("fee", ValueFromAmount(nFee)));
|
contextInfo.push_back(Pair("fee", ValueFromAmount(nFee)));
|
||||||
|
|
||||||
// Contextual transaction we will build on
|
// Contextual transaction we will build on
|
||||||
|
int nextBlockHeight = chainActive.Height() + 1;
|
||||||
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(
|
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(
|
||||||
Params().GetConsensus(),
|
Params().GetConsensus(), nextBlockHeight);
|
||||||
chainActive.Height() + 1);
|
|
||||||
if (contextualTx.nVersion == 1) {
|
if (contextualTx.nVersion == 1) {
|
||||||
contextualTx.nVersion = 2; // Tx format should support vjoinsplits
|
contextualTx.nVersion = 2; // Tx format should support vjoinsplits
|
||||||
}
|
}
|
||||||
|
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
|
||||||
|
contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta;
|
||||||
|
}
|
||||||
|
|
||||||
// Create operation and add to global queue
|
// Create operation and add to global queue
|
||||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "coincontrol.h"
|
#include "coincontrol.h"
|
||||||
#include "consensus/upgrades.h"
|
#include "consensus/upgrades.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
|
#include "consensus/consensus.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
@ -2523,6 +2524,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC
|
||||||
|
|
||||||
CReserveKey reservekey(this);
|
CReserveKey reservekey(this);
|
||||||
CWalletTx wtx;
|
CWalletTx wtx;
|
||||||
|
|
||||||
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
|
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -2573,9 +2575,20 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
||||||
|
|
||||||
wtxNew.fTimeReceivedIsTxTime = true;
|
wtxNew.fTimeReceivedIsTxTime = true;
|
||||||
wtxNew.BindWallet(this);
|
wtxNew.BindWallet(this);
|
||||||
|
int nextBlockHeight = chainActive.Height() + 1;
|
||||||
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(
|
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(
|
||||||
Params().GetConsensus(), chainActive.Height() + 1);
|
Params().GetConsensus(), nextBlockHeight);
|
||||||
|
|
||||||
|
// Activates after Overwinter network upgrade
|
||||||
|
// Set nExpiryHeight to expiryDelta (default 20) blocks past current block height
|
||||||
|
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
|
||||||
|
if (nextBlockHeight + expiryDelta >= TX_EXPIRY_HEIGHT_THRESHOLD){
|
||||||
|
strFailReason = _("nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD.");
|
||||||
|
} else {
|
||||||
|
txNew.nExpiryHeight = nextBlockHeight + expiryDelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Discourage fee sniping.
|
// Discourage fee sniping.
|
||||||
//
|
//
|
||||||
// However because of a off-by-one-error in previous versions we need to
|
// However because of a off-by-one-error in previous versions we need to
|
||||||
|
|
Loading…
Reference in New Issue