Browse Source

Enable time locked coin bases to be used as normal coinbase transactions with longer maturity, fix max_money

pull/4/head
miketout 6 years ago
parent
commit
e980a26ddd
  1. 6
      src/chainparams.cpp
  2. 4
      src/komodo_globals.h
  3. 8
      src/komodo_utils.h
  4. 8
      src/main.cpp
  5. 7
      src/metrics.cpp
  6. 30
      src/primitives/transaction.cpp
  7. 2
      src/primitives/transaction.h
  8. 4
      src/qt/transactiondesc.cpp
  9. 31
      src/script/script.cpp
  10. 9
      src/script/script.h
  11. 31
      src/script/script_ext.cpp
  12. 8
      src/script/script_ext.h
  13. 9
      src/txmempool.cpp
  14. 1
      src/wallet-utility.cpp
  15. 49
      src/wallet/wallet.cpp
  16. 6
      src/wallet/wallet.h

6
src/chainparams.cpp

@ -85,6 +85,7 @@ extern uint32_t ASSETCHAINS_MAGIC;
extern uint64_t ASSETCHAINS_SUPPLY;
extern uint64_t ASSETCHAINS_ALGO;
extern uint64_t ASSETCHAINS_EQUIHASH;
extern uint64_t ASSETCHAINS_VERUSHASH;
const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
@ -100,7 +101,10 @@ public:
consensus.nMajorityEnforceBlockUpgrade = 750;
consensus.nMajorityRejectBlockOutdated = 950;
consensus.nMajorityWindow = 4000;
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
consensus.powLimit = uint256S("00000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
else
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
consensus.nPowAveragingWindow = 17;
consensus.nMaxFutureBlockTime = 7 * 60; // 7 mins

4
src/komodo_globals.h

@ -117,14 +117,14 @@ int64_t komodo_current_supply(uint32_t nHeight)
int32_t baseid;
if ( (baseid = komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 )
cur_money = ASSETCHAINS_SUPPLY + nHeight * ASSETCHAINS_REWARD[0] / SATOSHIDEN;
cur_money = ASSETCHAINS_GENESISTXVAL + ASSETCHAINS_SUPPLY + nHeight * ASSETCHAINS_REWARD[0] / SATOSHIDEN;
else
{
// figure out max_money by adding up supply to a maximum of 10,000,000 blocks
cur_money = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff) + ASSETCHAINS_GENESISTXVAL;
if ( ASSETCHAINS_LASTERA == 0 && ASSETCHAINS_REWARD[0] == 0 )
{
cur_money = ASSETCHAINS_SUPPLY + (nHeight * 10000) / SATOSHIDEN;
cur_money += (nHeight * 10000) / SATOSHIDEN;
}
else
{

8
src/komodo_utils.h

@ -1733,6 +1733,11 @@ void komodo_args(char *argv0)
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_DECAY[i]),(void *)&ASSETCHAINS_DECAY[i]);
}
if (ASSETCHAINS_LASTERA > 0)
{
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_LASTERA),(void *)&ASSETCHAINS_LASTERA);
}
// hash in lock above for time locked coinbase transactions above a certain reward value only if the lock above
// param was specified, otherwise, for compatibility, do nothing
if ( ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF )
@ -1777,7 +1782,8 @@ void komodo_args(char *argv0)
if ( (port= komodo_userpass(ASSETCHAINS_USERPASS,ASSETCHAINS_SYMBOL)) != 0 )
ASSETCHAINS_RPCPORT = port;
else komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1);
COINBASE_MATURITY = 1;
if (ASSETCHAINS_LASTERA == 0 && ASSETCHAINS_REWARD[0] == 0)
COINBASE_MATURITY = 1;
//fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_RPCPORT);
}
if ( ASSETCHAINS_RPCPORT == 0 )

8
src/main.cpp

@ -2056,6 +2056,14 @@ namespace Consensus {
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
}
// ensure that zeroth output of coinbases are not still time locked
uint64_t unlockTime = tx.UnlockTime(0);
if (nSpendHeight >= unlockTime) {
return state.Invalid(
error("CheckInputs(): tried to spend coinbase that is timelocked until block %d", unlockTime),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
}
// Ensure that coinbases cannot be spent to transparent outputs
// Disabled on regtest

7
src/metrics.cpp

@ -24,6 +24,9 @@
#endif
#include <unistd.h>
extern int64_t ASSETCHAINS_TIMELOCKGTE;
int64_t komodo_block_unlocktime(uint32_t nHeight);
void AtomicTimer::start()
{
std::unique_lock<std::mutex> lock(mtx);
@ -335,7 +338,9 @@ int printMetrics(size_t cols, bool mining)
if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
subsidy -= subsidy/5;
}
if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
if ((std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) ||
(tipHeight < komodo_block_unlocktime(height) && subsidy >= ASSETCHAINS_TIMELOCKGTE)) {
immature += subsidy;
} else {
mature += subsidy;

30
src/primitives/transaction.cpp

@ -266,6 +266,36 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
return nTxSize;
}
// will return the open time or block if this is a time locked transaction output that we recognize.
// if we can't determine that it has a valid time lock, it returns 0
int64_t CTransaction::UnlockTime(uint32_t voutNum) const
{
if (vout.size() > voutNum + 1 && vout[voutNum].scriptPubKey.IsPayToScriptHash())
{
uint32_t voutNext = voutNum + 1;
std::vector<uint8_t> opretData;
uint160 scriptID = uint160(std::vector<unsigned char>(vout[voutNum].scriptPubKey.begin() + 2, vout[voutNum].scriptPubKey.begin() + 22));
CScript::const_iterator it = vout[voutNext].scriptPubKey.begin() + 1;
opcodetype op;
if (vout[voutNext].scriptPubKey.GetOp2(it, op, &opretData))
{
if (opretData.size() > 0 && opretData.data()[0] == OPRETTYPE_TIMELOCK)
{
int64_t unlocktime;
CScript opretScript = CScript(opretData.begin() + 1, opretData.end());
if (Hash160(opretScript) == scriptID &&
opretScript.IsCheckLockTimeVerify(&unlocktime))
{
return(unlocktime);
}
}
}
}
return(0);
}
std::string CTransaction::ToString() const
{
std::string str;

2
src/primitives/transaction.h

@ -457,6 +457,8 @@ public:
return (vin.size() == 1 && vin[0].prevout.IsNull());
}
int64_t UnlockTime(uint32_t voutNum) const;
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return a.hash == b.hash;

4
src/qt/transactiondesc.cpp

@ -267,8 +267,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
if ( ASSETCHAINS_SYMBOL[0] == 0 )
COINBASE_MATURITY = _COINBASE_MATURITY;
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks and have any applicable time locks open before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
// we need to display any possible CLTV lock time
}

31
src/script/script.cpp

@ -284,6 +284,37 @@ bool CScript::IsPushOnly() const
return true;
}
// if the front of the script has check lock time verify. this is a fairly simple check.
// accepts NULL as parameter if unlockTime is not needed.
bool CScript::IsCheckLockTimeVerify(int64_t *unlockTime) const
{
opcodetype op;
std::vector<unsigned char> unlockTimeParam = std::vector<unsigned char>();
CScript::const_iterator it = this->begin();
if (this->GetOp2(it, op, &unlockTimeParam))
{
if (unlockTimeParam.size() >= 0 && unlockTimeParam.size() < 6 &&
*(this->data() + unlockTimeParam.size() + 1) == OP_CHECKLOCKTIMEVERIFY)
{
int i = unlockTimeParam.size() - 1;
for (*unlockTime = 0; i >= 0; i--)
{
*unlockTime <<= 8;
*unlockTime |= *((unsigned char *)unlockTimeParam.data() + i);
}
return true;
}
}
return false;
}
bool CScript::IsCheckLockTimeVerify() const
{
int64_t ult;
return this->IsCheckLockTimeVerify(&ult);
}
std::string CScript::ToString() const
{
std::string str;

9
src/script/script.h

@ -17,6 +17,8 @@
#include <string>
#include <vector>
#define OPRETTYPE_TIMELOCK 1
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes
// Max size of pushdata in a CC sig in bytes
@ -575,6 +577,13 @@ public:
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
bool IsPushOnly() const;
/** if the front of the script has check lock time verify. this is a fairly simple check.
* accepts NULL as parameter if unlockTime is not needed.
*/
bool IsCheckLockTimeVerify(int64_t *unlockTime) const;
bool IsCheckLockTimeVerify() const;
/**
* Returns whether the script is guaranteed to fail at execution,
* regardless of the initial stack. This allows outputs to be pruned

31
src/script/script_ext.cpp

@ -74,34 +74,3 @@ const CScriptExt &CScriptExt::TimeLockSpend(const CKeyID &key, int64_t unlocktim
return *this;
}
// if the front of the script has check lock time verify. this is a fairly simple check.
// accepts NULL as parameter if unlockTime is not needed.
bool CScriptExt::IsCheckLockTimeVerify(int64_t *unlockTime) const
{
opcodetype op;
std::vector<unsigned char> unlockTimeParam = std::vector<unsigned char>();
CScript::const_iterator it = this->begin();
if (this->GetOp2(it, op, &unlockTimeParam))
{
if (unlockTimeParam.size() >= 0 && unlockTimeParam.size() < 6 &&
*(this->data() + unlockTimeParam.size() + 1) == OP_CHECKLOCKTIMEVERIFY)
{
int i = unlockTimeParam.size() - 1;
for (*unlockTime = 0; i >= 0; i--)
{
*unlockTime <<= 8;
*unlockTime |= *((unsigned char *)unlockTimeParam.data() + i);
}
return true;
}
}
return false;
}
bool CScriptExt::IsCheckLockTimeVerify() const
{
int64_t ult;
return this->IsCheckLockTimeVerify(&ult);
}

8
src/script/script_ext.h

@ -11,8 +11,6 @@
#include "standard.h"
#include "pubkey.h"
#define OPRETTYPE_TIMELOCK 1
class CScriptExt : public CScript
{
public:
@ -38,12 +36,6 @@ class CScriptExt : public CScript
// combined CLTV script and P2PKH
const CScriptExt &TimeLockSpend(const CKeyID &key, int64_t unlocktime) const;
// if the front of the script has check lock time verify. this is a fairly simple check.
// accepts NULL as parameter if unlockTime is not needed.
bool IsCheckLockTimeVerify(int64_t *unlockTime) const;
bool IsCheckLockTimeVerify() const;
};
#endif

9
src/txmempool.cpp

@ -312,6 +312,9 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
}
}
extern int64_t ASSETCHAINS_TIMELOCKGTE;
int64_t komodo_block_unlocktime(uint32_t nHeight);
void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags)
{
// Remove transactions spending a coinbase which are now immature
@ -331,8 +334,10 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
if (it2 != mapTx.end())
continue;
const CCoins *coins = pcoins->AccessCoins(txin.prevout.hash);
if (nCheckFrequency != 0) assert(coins);
if (!coins || (coins->IsCoinBase() && ((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY)) {
if (nCheckFrequency != 0) assert(coins);
if (!coins || (coins->IsCoinBase() && (((signed long)nMemPoolHeight) - coins->nHeight < COINBASE_MATURITY) &&
((signed long)nMemPoolHeight < komodo_block_unlocktime(coins->nHeight) &&
coins->IsAvailable(0) && coins->vout[0].nValue >= ASSETCHAINS_TIMELOCKGTE))) {
transactionsToRemove.push_back(tx);
break;
}

1
src/wallet-utility.cpp

@ -17,6 +17,7 @@ uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT;
uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC;
uint32_t ASSETCHAINS_MAGIC = 2387029918;
uint32_t ASSETCHAINS_EQUIHASH = 0;
uint32_t ASSETCHAINS_VERUSHASH = 1;
uint32_t ASSETCHAINS_ALGO = 0;
unsigned int MAX_BLOCK_SIGOPS = 20000;

49
src/wallet/wallet.cpp

@ -1206,7 +1206,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
bool fExisted = mapWallet.count(tx.GetHash()) != 0;
if (fExisted && !fUpdate) return false;
auto noteData = FindMyNotes(tx);
if (fExisted || IsMineOrWatch(tx) || IsFromMe(tx) || noteData.size() > 0)
if (fExisted || IsMine(tx) || IsFromMe(tx) || noteData.size() > 0)
{
CWalletTx wtx(this,tx);
@ -1468,49 +1468,18 @@ bool CWallet::IsMine(const CTransaction& tx)
return false;
}
bool CWallet::IsMineOrWatch(const CTransaction& tx)
{
for (int i = 0; i < tx.vout.size(); i++)
{
if (IsMine(tx, i) & ISMINE_ALL)
return true;
}
return false;
}
// special case handling for CLTV scripts, this does not error check to ensure the script is CLTV and is
// only internal to the wallet for that reason. if it is the first time we see this script, we add it to the wallet.
isminetype CWallet::IsCLTVMine(CScriptExt &script, CScriptID &scriptID, int64_t locktime)
// only internal to the wallet for that reason.
isminetype CWallet::IsCLTVMine(CScript &script, CScriptID &scriptID, int64_t locktime) const
{
uint8_t pushOp = script.data()[0];
uint32_t scriptStart = pushOp + 3;
// check post CLTV script
CScriptExt postfix = CScriptExt(script.size() > scriptStart ? script.begin() + scriptStart : script.end(), script.end());
CScript postfix = CScript(script.size() > scriptStart ? script.begin() + scriptStart : script.end(), script.end());
// check again with postfix subscript
isminetype ret = ::IsMine(*this, postfix);
if (ret == ISMINE_SPENDABLE)
{
// once we get here, we should have this script in our
// wallet, either as watch only if still time locked, or spendable
CBlockIndex &tip = *(chainActive.Tip());
if (!(locktime < LOCKTIME_THRESHOLD ? tip.nHeight >= locktime : tip.GetBlockTime() >= locktime))
{
ret = ISMINE_WATCH_ONLY;
if (!this->HaveWatchOnly(script))
{
this->AddWatchOnly(script);
}
} else
{
if (this->HaveWatchOnly(script))
this->RemoveWatchOnly(script);
if (!this->HaveCScript(scriptID))
this->AddCScript(script);
}
}
return ret;
return(::IsMine(*this, postfix));
}
typedef vector<unsigned char> valtype;
@ -1584,7 +1553,7 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum)
if (opretData.size() > 0 && opretData.data()[0] == OPRETTYPE_TIMELOCK)
{
int64_t unlocktime;
CScriptExt opretScript = CScriptExt(opretData.begin() + 1, opretData.end());
CScript opretScript = CScriptExt(opretData.begin() + 1, opretData.end());
if (CScriptID(opretScript) == scriptID &&
opretScript.IsCheckLockTimeVerify(&unlocktime))
@ -3952,10 +3921,12 @@ int CMerkleTx::GetBlocksToMaturity() const
COINBASE_MATURITY = _COINBASE_MATURITY;
if (!IsCoinBase())
return 0;
return max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());
uint32_t depth = GetDepthInMainChain();
uint32_t ui;
uint32_t toMaturity = (ui = UnlockTime(0) - chainActive.Height()) < 0 ? 0 : ui;
return((ui = COINBASE_MATURITY - depth) < toMaturity ? toMaturity : ui);
}
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
{
CValidationState state;

6
src/wallet/wallet.h

@ -277,7 +277,6 @@ struct CNotePlaintextEntry
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx : public CTransaction
{
@ -762,7 +761,7 @@ protected:
private:
template <class T>
void SyncMetaData(std::pair<typename TxSpendMap<T>::iterator, typename TxSpendMap<T>::iterator>);
isminetype IsCLTVMine(CScriptExt &script, CScriptID &scriptID, int64_t locktime);
isminetype IsCLTVMine(CScript &script, CScriptID &scriptID, int64_t locktime) const;
protected:
bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx);
@ -904,14 +903,12 @@ public:
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
bool IsLockedNote(uint256 hash, size_t js, uint8_t n) const;
void LockNote(JSOutPoint& output);
void UnlockNote(JSOutPoint& output);
void UnlockAllNotes();
std::vector<JSOutPoint> ListLockedNotes();
/**
* keystore implementation
* Generate a new key
@ -1056,7 +1053,6 @@ public:
bool IsChange(const CTxOut& txout) const;
CAmount GetChange(const CTxOut& txout) const;
bool IsMine(const CTransaction& tx);
bool IsMineOrWatch(const CTransaction& tx);
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;

Loading…
Cancel
Save