diff --git a/src/cc/CCHeir.h b/src/cc/CCHeir.h index ecaff9cdb..30334f6e1 100644 --- a/src/cc/CCHeir.h +++ b/src/cc/CCHeir.h @@ -27,10 +27,10 @@ bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, class CoinHelper; class TokenHelper; -UniValue HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); -UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); -UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount); -UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount); +UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); +UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string amount); +UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string amount); UniValue HeirInfo(uint256 fundingtxid); UniValue HeirList(); diff --git a/src/cc/heir.cpp b/src/cc/heir.cpp index ccf9da1bf..71ec9b040 100644 --- a/src/cc/heir.cpp +++ b/src/cc/heir.cpp @@ -56,13 +56,14 @@ template bool RunValidationPlans(uint8_t funcId, struct CCcont // only for tokens: CMyPubkeyVoutValidator ownerCCaddrValidator(cp, fundingOpretScript, false); // check if this correct owner's cc user addr corresponding to opret COpRetValidator opRetValidator(cp, fundingOpretScript); // compare opRets in this and last tx + CMarkerValidator markerValidator(cp); // initial tx marker spending protection CNullValidator nullValidator(cp); switch (funcId) { - case 'F': // fund tokens + case 'F': // fund tokens (only for tokens) // vin validation plan: vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &ownerCCaddrValidator); // check cc owner addr // vout validation plan: voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding @@ -70,10 +71,10 @@ template bool RunValidationPlans(uint8_t funcId, struct CCcont // no checking for opret yet break; - case 'A': // add tokens + case 'A': // add tokens (only for tokens) // vin validation plan: vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &ownerCCaddrValidator); // check cc owner addr // vout validation plan: voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding @@ -81,10 +82,10 @@ template bool RunValidationPlans(uint8_t funcId, struct CCcont voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A: break; - case 'C': + case 'C': // spend coins or tokens // vin validation plan: vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &cc1of2ValidatorThis); // cc1of2 funding addr + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &markerValidator, &cc1of2ValidatorThis); // cc1of2 funding addr // vout validation plan: voutPlan.pushValidators(0, &heirSpendValidator); // check if heir is allowed to spend @@ -400,6 +401,23 @@ uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &f return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); } +// check if pubkey is in vins +void CheckVinPubkey(std::vector vins, CPubKey pubkey, bool &hasPubkey, bool &hasOtherPubkey) { + + hasPubkey = false; + hasOtherPubkey = false; + + for (auto vin : vins) { + CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig); + if (vinPubkey.IsValid()) { + if (vinPubkey == pubkey) + hasPubkey = true; + if (vinPubkey != pubkey) + hasOtherPubkey = true; + } + } +} + /** * find the latest funding tx: it may be the first F tx or one of A or C tx's * Note: this function is also called from validation code (use non-locking calls) @@ -471,10 +489,20 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke if (tmpFuncId != 0 && fundingtxid == fundingTxidInOpret && (tokenid == zeroid || tokenid == tokenidInOpret)) { // check tokenid also if (blockHeight > maxBlockHeight) { - maxBlockHeight = blockHeight; - latesttxid = txid; - funcId = tmpFuncId; - hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; + + // check owner pubkey in vins + bool isOwner = false; + bool isNonOwner = false; + + CheckVinPubkey(regtx.vin, ownerPubkey, isOwner, isNonOwner); + + // we ignore 'donations' tx (with non-owner inputs) for calculating if heir is allowed to spend: + if (isOwner && !isNonOwner) { + hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; + maxBlockHeight = blockHeight; + latesttxid = txid; + funcId = tmpFuncId; + } //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; @@ -615,7 +643,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info * and also for setting spending plan for the funds' owner and heir * @return fundingtxid handle for subsequent references to this heir funding plan */ -template UniValue HeirFund(uint64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) +template UniValue _HeirFund(int64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -624,27 +652,33 @@ template UniValue HeirFund(uint64_t txfee, int64_t amount, std cp = CCinit(&C, Helper::getMyEval()); if (txfee == 0) txfee = 10000; + + int64_t markerfee = 10000; //std::cerr << "HeirFund() amount=" << amount << " txfee=" << txfee << " heirPubkey IsValid()=" << heirPubkey.IsValid() << " inactivityTime(sec)=" << inactivityTimeSec << " tokenid=" << tokenid.GetHex() << std::endl; if (!heirPubkey.IsValid()) { std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; - return std::string(""); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid heir pubkey")); } CPubKey myPubkey = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners + if (AddNormalinputs(mtx, myPubkey, markerfee, 3) > 0) { int64_t inputs, change; - if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { // 2 x txfee: 1st for marker vout, 2nd to miners - //mtx.vout.push_back(MakeTokensCC1of2vout(/*Helper::getMyEval()*/EVAL_HEIR, amount, myPubkey, heirPubkey)); // add cryptocondition to spend amount for either pk - mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); + if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { + + mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); // add a marker for finding all plans in HeirList() // TODO: change marker either to cc or normal txidaddr unspendable - CPubKey heirUnspendablePubKey = GetUnspendable(cp, 0); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); // TODO: do we need this marker? + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + CPubKey heirUnspendablePubKey = GetUnspendable(cpHeir, 0); + // mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); <-- bad marker cause it was spendable by anyone + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, markerfee, heirUnspendablePubKey)); // this marker spending is disabled in the validation code // calc and add change vout: if (inputs > amount) @@ -655,7 +689,20 @@ template UniValue HeirFund(uint64_t txfee, int64_t amount, std if (change != 0) { // vout[1] mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); } - + + // check owner pubkey in vins + bool hasMypubkey = false; + bool hasNotMypubkey = false; + + CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); + + // for initial funding do not allow to sign by non-owner key: + if (hasNotMypubkey) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "using non-owner inputs not allowed")); + return result; + } + // add 1of2 vout validation pubkeys: std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(myPubkey); @@ -689,12 +736,12 @@ template UniValue HeirFund(uint64_t txfee, int64_t amount, std } // if no these callers - it could not link -UniValue HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); +UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ + return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); } -UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { + return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); } /** @@ -702,7 +749,7 @@ UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName * creates tx to add more funds to cryptocondition address for spending by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -715,6 +762,8 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i if (txfee == 0) txfee = 10000; + int64_t markerfee = 10000; + CPubKey myPubkey = pubkey2pk(Mypubkey()); // check if it is the owner @@ -724,7 +773,7 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i return result; } - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners + if (AddNormalinputs(mtx, myPubkey, markerfee, 3) > 0) { // some for marker int64_t inputs, change; @@ -739,7 +788,11 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i // add cryptocondition to spend this funded amount for either pk mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); - + + char markeraddr[64]; + CPubKey markerPubkey = CCtxidaddr(markeraddr, fundingtxid); + mtx.vout.push_back(CTxOut(markerfee, CScript() << ParseHex(HexStr(markerPubkey)) << OP_CHECKSIG)); // marker to prevent archiving of the funds add vouts + if (inputs > amount) change = (inputs - amount); // -txfee <-- txfee pays user @@ -748,7 +801,29 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i if (change != 0) { // vout[1] mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); } + + // check owner pubkey in vins + bool hasMypubkey = false; + bool hasNotMypubkey = false; + + CheckVinPubkey(mtx.vin, myPubkey, hasMypubkey, hasNotMypubkey); + + // for additional funding do not allow to sign by both owner and non-owner keys (is this a donation or not?): + if (hasMypubkey && hasNotMypubkey) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "using both owner and non-owner inputs is not allowed")); + return result; + } + // warn the user he's making a donation if this is all non-owner keys: + if (hasNotMypubkey) { + result.push_back(Pair("result", "warning")); + result.push_back(Pair("warning", "you are about to make a donation to heir fund")); + } + else { + result.push_back(Pair("result", "success")); + } + // add 1of2 vout validation pubkeys - needed only for tokens: std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(ownerPubkey); @@ -759,11 +834,11 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); result.push_back(Pair("hextx", rawhextx)); } else { std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.clear(); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "sign error")); } @@ -781,13 +856,11 @@ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, i result.push_back(Pair("error", "can't find normal inputs for tx fee")); } - - return result; } -UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { +UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount) { CPubKey ownerPubkey, heirPubkey; int64_t inactivityTimeSec; @@ -798,10 +871,27 @@ UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { uint8_t hasHeirSpendingBegun = 0; if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if (tokenid == zeroid) - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + if (tokenid == zeroid) { + int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } + + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + } + else { + int64_t amount = atoll(strAmount.c_str()); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + } } else { UniValue result(UniValue::VOBJ); @@ -819,7 +909,7 @@ UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { * creates tx to spend funds from cryptocondition address by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -940,7 +1030,7 @@ template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfe return result; } -UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { +UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount) { CPubKey ownerPubkey, heirPubkey; int64_t inactivityTimeSec; @@ -951,10 +1041,26 @@ UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { uint8_t hasHeirSpendingBegun = 0; if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if( tokenid == zeroid ) - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + if (tokenid == zeroid) { + int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); + if (amount < 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + } + else { + int64_t amount = atoll(strAmount.c_str()); + if (amount <= 0) { + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid amount")); + return result; + } + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + } } else { @@ -1022,6 +1128,8 @@ UniValue HeirInfo(uint256 fundingtxid) std::ostringstream stream; std::string msg; + //sleep(10); + result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); result.push_back(Pair("name", heirName.c_str())); @@ -1090,19 +1198,27 @@ UniValue HeirInfo(uint256 fundingtxid) } stream << inactivityTimeSec; - result.push_back(Pair("inactivity time setting", stream.str().c_str())); + result.push_back(Pair("inactivity time setting, sec", stream.str().c_str())); stream.str(""); stream.clear(); if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun durationSec = CCduration(numblocks, latestFundingTxid); - std::cerr << "HeirInfo() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; + std::cerr << "HeirInfo() duration (sec)=" << durationSec << " inactivityTime (sec)=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; } stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); stream.str(""); stream.clear(); + + // adding owner current inactivity time: + if (!hasHeirSpendingBegun && durationSec <= inactivityTimeSec) { + stream << durationSec; + result.push_back(Pair("owner inactivity time, sec", stream.str().c_str())); + stream.str(""); + stream.clear(); + } result.push_back(Pair("result", "success")); } @@ -1123,15 +1239,15 @@ UniValue HeirInfo(uint256 fundingtxid) * @return list of heir plan handles (fundingtxid) */ -template void _HeirList(struct CCcontract_info *cp, UniValue &result) +void _HeirList(struct CCcontract_info *cp, UniValue &result) { std::vector> unspentOutputs; - char coinaddr[64]; - CPubKey ccPubKeyEmpty; - GetCCaddress(cp, coinaddr, ccPubKeyEmpty); - SetCCunspents(unspentOutputs, cp->normaladdr); + char markeraddr[64]; + + GetCCaddress(cp, markeraddr, GetUnspendable(cp, NULL)); + SetCCunspents(unspentOutputs, markeraddr); - //std::cerr << "HeirList() finding heir marker from Heir contract addr=" << cp->normaladdr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; + //std::cerr << "HeirList() finding heir marker from unspendable addr=" << markeraddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; // TODO: move marker to special cc addr to prevent checking all tokens for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { @@ -1177,10 +1293,10 @@ UniValue HeirList() struct CCcontract_info *cpHeir, *cpTokens, heirC, tokenC; // NOTE we must use a separate 'C' structure for each CCinit! cpHeir = CCinit(&heirC, EVAL_HEIR); - cpTokens = CCinit(&tokenC, EVAL_TOKENS); + //cpTokens = CCinit(&tokenC, EVAL_TOKENS); - _HeirList(cpHeir, result); - _HeirList(cpTokens, result); + _HeirList(cpHeir, result); + //_HeirList(cpTokens, result); not used anymore return result; } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index c4bb7ea60..df58e3bf1 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -125,8 +125,8 @@ class CValidatorBase public: CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} virtual bool isVinValidator() const = 0; - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; - virtual bool validateVout(CTxOut vout, std::string& message) const = 0; + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const = 0; + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0; protected: CCcontract_info * m_cp; @@ -258,10 +258,10 @@ private: if (v->isVinValidator()) // validate this vin and previous vout: - result = v->validateVin(pTx->vin[iv], pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); + result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage); else // if it is vout validator pass the previous tx vout: - result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); + result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage); if (!result) { return result; } @@ -359,7 +359,7 @@ private: if (!v->isVinValidator()) { // if this is a 'in' validation plan then pass the previous tx vout: - bool result = v->validateVout(pTx->vout[iv], refMessage); + bool result = v->validateVout(pTx->vout[iv], iv, refMessage); if (!result) return result; } @@ -402,7 +402,7 @@ public: m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; CPubKey ownerPubkey, heirPubkey; @@ -438,7 +438,7 @@ public: std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; return false; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return false; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return false; } private: CScript m_fundingOpretScript; @@ -456,7 +456,7 @@ public: : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; @@ -498,7 +498,7 @@ public: message = std::string("invalid pubkey"); return false; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; @@ -516,7 +516,7 @@ public: : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; @@ -554,7 +554,7 @@ public: // this is not heir: return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; @@ -572,7 +572,7 @@ public: : m_fundingOpretScript(opret), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; @@ -607,12 +607,48 @@ public: //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; }; + +/** + * marker spending prevention validator, + * returns false if for tx with funcid=F vout.1 is being tried to spend + */ +template class CMarkerValidator : CValidatorBase +{ +public: + CMarkerValidator(CCcontract_info* cp) + : CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return true; } // this is vin validator + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { + + uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; + uint8_t dummyIsHeirSpendingBegan; + + //std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl; + if (prevVout.size() > 0) { + + // get funcId for prev tx: + uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); + + //std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl; + + if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout + message = std::string("spending marker not allowed"); + return false; + } + } + //std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl; + return true; + } +}; + /** * empty validator always returns true */ @@ -623,8 +659,8 @@ public: : CValidatorBase(cp) { } virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } }; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 24f0c5f98..82a400e71 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -537,6 +537,9 @@ static const CRPCCommand vRPCCommands[] = { "util", "reconsiderblock", &reconsiderblock, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, + { "hidden", "test_ac", &test_ac, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + #ifdef ENABLE_WALLET /* Wallet */ { "wallet", "resendwallettransactions", &resendwallettransactions, true}, diff --git a/src/rpc/server.h b/src/rpc/server.h index 0166bca41..3edc85ebf 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -471,4 +471,8 @@ extern UniValue paxprices(const UniValue& params, bool fHelp); extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); +// test rpc: +extern UniValue test_ac(const UniValue& params, bool fHelp); +extern UniValue test_heirmarker(const UniValue& params, bool fHelp); + #endif // BITCOIN_RPCSERVER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8180c9239..c1cf25b41 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -7362,7 +7362,7 @@ UniValue heirfund(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); uint256 tokenid = zeroid; - uint64_t txfee; + int64_t txfee; int64_t amount; int64_t inactivitytime; std::string hex; @@ -7373,21 +7373,34 @@ UniValue heirfund(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 5 && params.size() != 6) - throw runtime_error("heirfundtokens fee funds heirname heirpubkey inactivitytime [tokenid]\n"); + throw runtime_error("heirfund txfee funds heirname heirpubkey inactivitytime [tokenid]\n"); if (ensure_CCrequirements() < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + + if(params.size() == 6) // tokens in satoshis: + amount = atoll(params[1].get_str().c_str()); + else // coins: + amount = atof(params[1].get_str().c_str()) * COIN; + + if( amount <= 0 ) + throw runtime_error("incorrect amount\n"); + name = params[2].get_str(); pubkey = ParseHex(params[3].get_str().c_str()); if( !pubkey2pk(pubkey).IsValid() ) throw runtime_error("incorrect pubkey\n"); - inactivitytime = atof((char*)params[4].get_str().c_str()); + inactivitytime = atoll(params[4].get_str().c_str()); + if (inactivitytime <= 0) + throw runtime_error("incorrect inactivity time param\n"); + if (params.size() == 6) { tokenid = Parseuint256((char*)params[5].get_str().c_str()); if(tokenid == zeroid) @@ -7406,7 +7419,7 @@ UniValue heiradd(const UniValue& params, bool fHelp) { UniValue result; uint256 fundingtxid; - uint64_t txfee; + int64_t txfee; int64_t amount; int64_t inactivitytime; std::string hex; @@ -7417,18 +7430,20 @@ UniValue heiradd(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 3) - throw runtime_error("heiraddtokens fee funds fundingtxid\n"); + throw runtime_error("heiradd txfee funds fundingtxid\n"); if (ensure_CCrequirements() < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); - result = HeirAddCaller(fundingtxid, txfee, amount); + result = HeirAddCaller(fundingtxid, txfee, params[1].get_str()); return result; } @@ -7437,7 +7452,6 @@ UniValue heirclaim(const UniValue& params, bool fHelp) UniValue result; // result(UniValue::VOBJ); uint256 fundingtxid; int64_t txfee; - int64_t amount; int64_t inactivitytime; std::string hex; std::vector pubkey; @@ -7448,18 +7462,20 @@ UniValue heirclaim(const UniValue& params, bool fHelp) return NullUniValue; if (fHelp || params.size() != 3) - throw runtime_error("heirclaimtokens fee funds fundingtxid\n"); + throw runtime_error("heirclaim txfee funds fundingtxid\n"); if (ensure_CCrequirements() < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); - txfee = atoll((char*)params[0].get_str().c_str()); - amount = atoll((char*)params[1].get_str().c_str()); + txfee = atoll(params[0].get_str().c_str()); + if (txfee < 0) + throw runtime_error("incorrect txfee param\n"); + fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); - result = HeirClaimCaller(fundingtxid, txfee, amount); + result = HeirClaimCaller(fundingtxid, txfee, params[1].get_str()); return result; } @@ -7581,3 +7597,79 @@ void RegisterWalletRPCCommands(CRPCTable &tableRPC) for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); } + +UniValue test_ac(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 4)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements() < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + std::vector pubkey1; + std::vector pubkey2; + + pubkey1 = ParseHex(params[0].get_str().c_str()); + pubkey2 = ParseHex(params[1].get_str().c_str()); + + CPubKey pk1 = pubkey2pk(pubkey1); + CPubKey pk2 = pubkey2pk(pubkey2); + + if(!pk1.IsValid() || !pk2.IsValid()) + throw runtime_error("invalid pubkey\n"); + + int64_t txfee = 10000; + int64_t amount = atoll(params[2].get_str().c_str()) * COIN; + uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); + + if( normalInputs < txfee + amount) + throw runtime_error("not enough normals\n"); + + mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); +} + +UniValue test_heirmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements() < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(fundingtxid, 1)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); +} \ No newline at end of file