diff --git a/src/cc/CoinbaseGuard.cpp b/src/cc/CoinbaseGuard.cpp index b7332bdc3..d1332ba84 100644 --- a/src/cc/CoinbaseGuard.cpp +++ b/src/cc/CoinbaseGuard.cpp @@ -10,18 +10,42 @@ */ #include "CoinbaseGuard.h" +#include "script/script.h" #include "main.h" extern int32_t VERUS_MIN_STAKEAGE; -bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> vData) +bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> &vData) { bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData); - if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS)) + if (isValid && vData.size() == 1 && vData[0][0] > 0 && vData[0][0] < 4) { - return true; + CScript data = CScript(vData[0].begin(), vData[0].end()); + vData.clear(); + + uint32_t bytesTotal; + CScript::const_iterator pc = data.begin(); + std::vector vch = std::vector(); + opcodetype op; + bool moreData = true; + + for (bytesTotal = vch.size(); + bytesTotal <= nMaxDatacarrierBytes && !(isValid = (pc == data.end())) && (moreData = data.GetOp(pc, op, vch)) && (op > 0 && op <= OP_PUSHDATA4); + bytesTotal += vch.size()) + { + vData.push_back(vch); + } + + // if we ran out of data, we're ok + if (isValid) + + if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS)) + { + return true; + } } + return false; } @@ -39,7 +63,7 @@ CStakeParams::CStakeParams(std::vector> vData) vData[0][0] == OPRETTYPE_STAKEPARAMS && vData[1].size() <= 4 && vData[2].size() <= 4 && vData[3].size() == sizeof(prevHash) && - (vData.size() == STAKE_MINPARAMS || vData[4].size() == 20 || vData[5].size() == 33)) + (vData.size() == STAKE_MINPARAMS || (vData.size() == STAKE_MAXPARAMS && vData[4].size() == 33))) { for (auto ch : vData[1]) { @@ -54,20 +78,12 @@ CStakeParams::CStakeParams(std::vector> vData) if (vData.size() == 4) { - dest = CTxDestination(); - } - else if (vData[4].size() == 20) - { - dest = CTxDestination(CKeyID(uint160(vData[4]))); + pk = CPubKey(); } else if (vData[4].size() == 33) { - CPubKey pk = CPubKey(vData[4]); - if (pk.IsValid()) - { - dest = pk; - } - else + pk = CPubKey(vData[4]); + if (!pk.IsValid()) { // invalidate srcHeight = 0; @@ -119,21 +135,11 @@ bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakePa (stakeParams.blkHeight - stakeParams.srcHeight >= VERUS_MIN_STAKEAGE) && Solver(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, txType, vAddr)) { - if (txType == TX_PUBKEY) + if (txType == TX_PUBKEY && !stakeParams.pk.IsValid()) { - if (stakeParams.dest.which() == 0) - { - stakeParams.dest = CPubKey(vAddr[0]); - } + stakeParams.pk = CPubKey(vAddr[0]); } - else if (txType == TX_PUBKEYHASH) - { - if (stakeParams.dest.which() == 0) - { - stakeParams.dest = CKeyID(uint160(vAddr[0])); - } - } - if ((txType == TX_PUBKEY) && (txType == TX_PUBKEYHASH)) + if ((txType == TX_PUBKEY) || (txType == TX_PUBKEYHASH && stakeParams.pk.IsFullyValid())) { auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus()); isValid = VerifyScript(stakeTx.vin[0].scriptSig, @@ -177,7 +183,7 @@ bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxO // version // utxo source hash // utxo source output - // hashed address of destination's pubkey + // destination's pubkey CKeyID key = dest.GetID(); vout.scriptPubKey << p.AsVector() << OP_DROP << a1 << OP_DROP << a2 << OP_DROP @@ -230,6 +236,7 @@ bool CoinbaseGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransa // return fail cc_free(cc); } + return true; } UniValue CoinbaseGuardInfo() diff --git a/src/cc/CoinbaseGuard.h b/src/cc/CoinbaseGuard.h index 9fdb562a5..a04ce8b2b 100644 --- a/src/cc/CoinbaseGuard.h +++ b/src/cc/CoinbaseGuard.h @@ -27,15 +27,36 @@ class CStakeParams uint32_t srcHeight; uint32_t blkHeight; uint256 prevHash; - CTxDestination dest; + CPubKey pk; - CStakeParams() : srcHeight(0), blkHeight(0), prevHash(), dest() {} + CStakeParams() : srcHeight(0), blkHeight(0), prevHash(), pk() {} CStakeParams(std::vector> vData); + CStakeParams(uint32_t _srcHeight, uint32_t _blkHeight, const uint256 &_prevHash, const CPubKey &_pk) : + srcHeight(_srcHeight), blkHeight(_blkHeight), prevHash(_prevHash), pk(_pk) {} + + std::vector AsVector() + { + std::vector ret; + CScript scr = CScript() << OPRETTYPE_STAKEPARAMS + << srcHeight << blkHeight + << std::vector(prevHash.begin(), prevHash.end()); + + if (pk.IsValid()) + { + scr << std::vector(pk.begin(), pk.end()); + } + + ret = std::vector(scr.begin(), scr.end()); + return ret; + } + bool IsValid() { return srcHeight != 0; } }; +bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> &vData); + bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams); bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout); diff --git a/src/miner.cpp b/src/miner.cpp index 72eb3f6f9..f0d4ea81d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -10,6 +10,7 @@ #include "amount.h" #include "chainparams.h" +#include "cc/CoinbaseGuard.h" #include "importcoin.h" #include "consensus/consensus.h" #include "consensus/upgrades.h" @@ -127,12 +128,22 @@ int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_ int64_t komodo_block_unlocktime(uint32_t nHeight); uint64_t komodo_commission(const CBlock *block); int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig); -int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig); +int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { CScript scriptPubKeyIn(_scriptPubKeyIn); + + CPubKey pk = CPubKey(); + std::vector> vAddrs; + txnouttype txT; + if (Solver(scriptPubKeyIn, txT, vAddrs)) + { + if (txT == TX_PUBKEY) + pk = CPubKey(vAddrs[0]); + } + uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); //fprintf(stderr,"create new block\n"); // Create new block @@ -435,7 +446,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, uint32_t nBitsPOS; arith_uint256 posHash; - siglen = verus_staked(pblock, txStaked, nBitsPOS, posHash, utxosig); + siglen = verus_staked(pblock, txStaked, nBitsPOS, posHash, utxosig, pk); blocktime = GetAdjustedTime(); // change the scriptPubKeyIn to the same output script exactly as the staking transaction @@ -470,6 +481,21 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, txNew.vout.resize(1); txNew.vout[0].scriptPubKey = scriptPubKeyIn; txNew.vout[0].nValue = GetBlockSubsidy(nHeight,chainparams.GetConsensus()) + nFees; + + // once we get to Sapling, enable CC CoinbaseGuard for stake transactions + if (isStake && Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight >= nHeight) + { + // if there is a specific destination, use it + CTransaction stakeTx = pblock->vtx[pblock->vtx.size() - 1]; + CStakeParams p; + if (ValidateStakeTransaction(stakeTx, p)) + { + if (!MakeGuardedOutput(txNew.vout[0].nValue, p.pk, stakeTx, txNew.vout[0])) + return 0; + } + else return 0; + } + txNew.nExpiryHeight = 0; txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); @@ -488,8 +514,13 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, // prepend time lock to original script unless original script is P2SH, in which case, we will leave the coins // protected only by the time lock rather than 100% inaccessible opretScript.AddCheckLockTimeVerify(komodo_block_unlocktime(nHeight)); - if (!scriptPubKeyIn.IsPayToScriptHash()) - opretScript += scriptPubKeyIn; + if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition()) + { + fprintf(stderr,"ERROR: attempt to add timelock to pay2sh or pay2cc\n"); + return 0; + } + + opretScript += scriptPubKeyIn; txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript)); txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK); diff --git a/src/script/script.cpp b/src/script/script.cpp index 5f56d15a1..8c338bc05 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -300,7 +300,7 @@ bool CScript::GetOpretData(std::vector>& vData) const } } -bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector>& vSolutions) const +bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector>& vParams) const { const_iterator pc = begin(); vector data; @@ -312,7 +312,7 @@ bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector> vSolutions; - return IsPayToCryptoCondition(pCCSubScript, vSolutions); + std::vector> vParams; + return IsPayToCryptoCondition(pCCSubScript, vParams); } bool CScript::IsPayToCryptoCondition() const diff --git a/src/script/sign.cpp b/src/script/sign.cpp index dec3f704e..bd4e88fdb 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -148,7 +148,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip // otherwise, push back the corresponding pub key vPK.push_back(CPubKey(ParseHex(C.CChexstr))); } - else if (vParams.size() >= (extraAddrs + 1)) + else if (vParams.size() > extraAddrs) { bool havePriv; vKeyID.push_back(CKeyID(uint160(vParams[1]))); @@ -195,7 +195,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip return ret.size() != 0; } } - else if (extraAddrs > 1 && vParams.size() >= (extraAddrs + 1)) + else if (extraAddrs > 1 && vParams.size() > extraAddrs) { // we need to get 2 addresses, and we will need the private key for one // to spend diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 273590e9b..534d8fe81 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5125,9 +5125,9 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt return(siglen); } -int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig) +int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk) { - return pwalletMain->VerusStakeTransaction(pBlock, txNew, nBits, hashResult, utxosig); + return pwalletMain->VerusStakeTransaction(pBlock, txNew, nBits, hashResult, utxosig, pk); } int32_t ensure_CCrequirements() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 964d8c0d4..5d5d8fcca 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -23,6 +23,7 @@ #include "crypter.h" #include "coins.h" #include "zcash/zip32.h" +#include "cc/CoinbaseGuard.h" #include @@ -44,6 +45,8 @@ bool fSendFreeTransactions = false; bool fPayAtLeastCustomFee = true; #include "komodo_defs.h" +extern int32_t USE_EXTERNAL_PUBKEY; +extern std::string NOTARY_PUBKEY; extern int32_t KOMODO_EXCHANGEWALLET; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern int32_t VERUS_MIN_STAKEAGE; @@ -1245,7 +1248,7 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, return false; } -int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig) const +int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey pk) const { CTransaction stakeSource; int32_t voutNum, siglen = 0; @@ -1257,6 +1260,9 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe tipindex = chainActive.LastTip(); bool extendedStake = tipindex->GetHeight() >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; + if (!extendedStake) + pk = CPubKey(); + bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus()); if (!VerusSelectStakeOutput(pBlock, hashResult, stakeSource, voutNum, tipindex->GetHeight() + 1, bnTarget) || @@ -1278,17 +1284,16 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe txNew.vin[0].prevout.hash = stakeSource.GetHash(); txNew.vin[0].prevout.n = voutNum; - CPubKey pk = CPubKey(); - if (whichType == TX_PUBKEY) { txNew.vout[0].scriptPubKey << ToByteVector(vSolutions[0]) << OP_CHECKSIG; - pk = CPubKey(vSolutions[0]); + if (!pk.IsValid()) + pk = CPubKey(vSolutions[0]); } else if (whichType == TX_PUBKEYHASH) { txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG; - if (extendedStake) + if (extendedStake && !pk.IsValid()) { // we need a pubkey, so try to get one from the key ID, if not there, fail if (!keystore.GetPubKey(CKeyID(uint160(vSolutions[0])), pk)) @@ -1299,7 +1304,7 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe return 0; // if we are staking with the extended format, add the opreturn data required - //if (extendedStake) + if (extendedStake) { uint256 srcBlock = uint256(); CBlockIndex *pSrcIndex; @@ -1314,14 +1319,7 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe return 0; txOut1.scriptPubKey << OP_RETURN - << (int8_t)OPRETTYPE_STAKEPARAMS - << pSrcIndex->GetHeight() << tipindex->GetHeight() - << std::vector(pBlock->hashPrevBlock.begin(), pBlock->hashPrevBlock.end()) - << std::vector(pk.begin(), pk.end()); - - // need to decide how to decide, but then we can add a delegated source here for the coinbase output - //if (USE_EXTERNAL_PUBKEY) - // txOut1.scriptPubKey << ParseHex(NOTARY_PUBKEY); + << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight(), pBlock->hashPrevBlock, pk).AsVector(); } nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 17d4b8d97..01fa7c4f0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1278,7 +1278,7 @@ public: // staking functions bool VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, uint32_t &bnTarget) const; - int32_t VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig) const; + int32_t VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey pk) const; /* Find unspent notes filtered by payment address, min depth and max depth */ void GetUnspentFilteredNotes(std::vector& sproutEntries, diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index 702f8e7da..75e93390f 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -9,6 +9,7 @@ #include "keystore.h" #include "script/script.h" #include "script/standard.h" +#include "cc/eval.h" #include @@ -83,6 +84,30 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& _scriptPubKey) break; } + case TX_CRYPTOCONDITION: + { + // some crypto conditions we consider "mine" if our address is the first specified + // extra address + CScript subScript; + vector vParams; + COptCCParams p; + if (scriptPubKey.IsPayToCryptoCondition(&subScript, vParams)) + { + if (vParams.size() > 1) + { + p = COptCCParams(vParams[0]); + // if we are the primary output on a coinbase guard, it is ours + if (p.IsValid() && p.evalCode == EVAL_COINBASEGUARD && vParams[1].size() == 20) + { + CKeyID adr = CKeyID(uint160(vParams[1])); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + } + } + } + break; + } + case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the