diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index a48195970..67bed7941 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -35,7 +35,7 @@ CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description); uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector &origpubkey); bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx); -int64_t IsAssetvout(int64_t &price,std::vector &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid); +int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid); bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); @@ -44,7 +44,8 @@ bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValu bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice); int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid); int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid); -bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid); +bool AssetExactAmounts(int32_t maxDepth, struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid); +//bool AssetExactAmounts(bool doValidateTx, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid, std::vector &ccVinsTxs); // CCassetstx int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index c9b975c3c..3fce95f0c 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -342,25 +342,54 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,co else return(false); } -int64_t IsAssetvout(int64_t &price,std::vector &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid) + +// Checks if the vout is a really Asset CC vout +// if maxAssetExactAmountDepth > 0, it also validates the vin transaction itself: +// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx +int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid) { uint256 assetid,assetid2; int64_t nValue=0; int32_t n; uint8_t funcid; + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) // maybe check address too? { - n = tx.vout.size(); + + if (maxAssetExactAmountDepth > 0) { + //validate all tx + int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; + std::vector ccVinsTxs; + + //std::cerr << "IsAssetvout() validate=yes" << std::endl; + const bool validateVinTxs = false; + bool isEqualAmounts = AssetExactAmounts(maxAssetExactAmountDepth, cp, myCCVinsAmount, 0, myCCVoutsAmount, eval, tx, refassetid); + + // if ccInputs != ccOutputs and it is not the tokenbase tx means it is possibly fake tx (dimxy): + if (!isEqualAmounts && refassetid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy) + std::cerr << "IsAssetvout() detected bad tx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx" << std::endl; + return 0; + } + } + + + n = tx.vout.size(); + if (v >= n - 1) { // just moved this up (dimxy) + std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; + return(0); + } nValue = tx.vout[v].nValue; - //fprintf(stderr,"CC vout v.%d of n.%d %.8f\n",v,n,(double)nValue/COIN); - if ( v >= n-1 ) - return(0); + + // fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f\n",v,n,(double)nValue/COIN); + if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 ) { - fprintf(stderr,"null decodeopret v.%d\n",v); + fprintf(stderr,"IsAssetvout() null decodeopret v.%d\n",v); return(0); } else if ( funcid == 'c' ) { - if ( refassetid == tx.GetHash() && v == 0 ) - return(nValue); + if (refassetid == tx.GetHash() && v == 0) { + std::cerr << "isAssetVout() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning nValue=" << nValue << std::endl; + return(nValue); + } } else if ( (funcid == 'b' || funcid == 'B') && v == 0 ) // critical! 'b'/'B' vout0 is NOT asset return(0); @@ -368,7 +397,7 @@ int64_t IsAssetvout(int64_t &price,std::vector &origpubkey,const CTrans { if ( assetid == refassetid ) { - //fprintf(stderr,"returning %.8f\n",(double)nValue/COIN); + fprintf(stderr,"IsAssetvout() returning %.8f\n",(double)nValue/COIN); return(nValue); } } @@ -443,29 +472,37 @@ int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpp fprintf(stderr,"AssetValidateSellvin\n"); if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 ) return(0); - if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 ) + if ( (assetoshis= IsAssetvout(1, cp, NULL, tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 ) return eval->Invalid("invalid missing CC vout0 for sellvin"); else return(assetoshis); } -bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid) + +// overload with additional params for deep tx validation (dimxy) +bool AssetExactAmounts(int maxDepth, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) { CTransaction vinTx; uint256 hashBlock,id,id2; int32_t i,flag,numvins,numvouts; int64_t assetoshis; std::vector tmporigpubkey; int64_t tmpprice; numvins = tx.vin.size(); numvouts = tx.vout.size(); inputs = outputs = 0; + + maxDepth--; + for (i=starti; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + { + //std::cerr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; + // we are really not inside validation! -- dimxy + if ( (eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) ) { - fprintf(stderr,"i.%d starti.%d numvins.%d\n",i,starti,numvins); - return eval->Invalid("always should find vin, but didnt"); - } - else if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 ) + fprintf(stderr,"AssetExactAmounts() cannot read vintx i.%d starti.%d numvins.%d\n", i,starti,numvins); + return (!eval) ? false : eval->Invalid("always should find vin, but didnt"); + + } // false means 'don't go deeper' -- dimxy + else if ( (assetoshis= IsAssetvout( maxDepth, cp, eval, tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 ) { - fprintf(stderr,"vin%d %llu, ",i,(long long)assetoshis); + fprintf(stderr,"AssetExactAmounts() vin%d %llu, ",i,(long long)assetoshis); inputs += assetoshis; } else @@ -473,33 +510,47 @@ bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti if ( vinTx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 && DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid ) { assetoshis = vinTx.vout[i].nValue; - fprintf(stderr,"vin%d %llu special case, ",i,(long long)assetoshis); + fprintf(stderr,"AssetExactAmounts() vin%d assetoshis=%llu special case, ",i,(long long)assetoshis); inputs += assetoshis; } } } } + + if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid ) flag = 1; else flag = 0; + for (i=0; i 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + fprintf(stderr,"AddAssetInputs() check destaddress=%s vout amount=%.8f\n",destaddr,(double)vintx.vout[vout].nValue/COIN); + if ( (nValue= IsAssetvout(1, cp, NULL, price,origpubkey,vintx,vout,assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; totalinputs += nValue; + //std::cerr << "AddAssetInputs() adding input nValue=" << nValue << std::endl; n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; } } } + + //std::cerr << "AddAssetInputs() found totalinputs=" << totalinputs << std::endl; return(totalinputs); } @@ -235,6 +238,11 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector des mask = ~((1LL << mtx.vin.size()) - 1); if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 ) { + + if (inputs < total) { //added dimxy + std::cerr << "AssetTransfer(): insufficient funds" << std::endl; + return (""); + } if ( inputs > total ) CCchange = (inputs - total); //for (i=0; i 0 ) { - if ( inputs < askamount ) - askamount = inputs; + if (inputs < askamount) { + //askamount = inputs; + std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl; + return (""); + } + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0))); if ( inputs > askamount ) CCchange = (inputs - askamount); @@ -333,9 +348,14 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey()); return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); - } else fprintf(stderr,"need some assets to place ask\n"); + } + else { + fprintf(stderr, "need some assets to place ask\n"); + } } - fprintf(stderr,"need some native coins to place ask\n"); + else { // dimxy added 'else', because it was misleading message before + fprintf(stderr, "need some native coins to place ask\n"); + } return(""); } @@ -373,9 +393,15 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a opret = EncodeAssetOpRet('e',assetid,assetid2,pricetotal,Mypubkey()); } return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); - } else fprintf(stderr,"need some assets to place ask\n"); + } + else { + fprintf(stderr, "need some assets to place ask\n"); + } } - fprintf(stderr,"need some native coins to place ask\n"); + else { // dimxy added 'else', because it was misleading message before + fprintf(stderr,"need some native coins to place ask\n"); + } + return(""); } diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 41a0ed7b1..378c427a9 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -129,6 +129,10 @@ vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] */ + + + +// tx validation bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { static uint256 zero; @@ -155,9 +159,11 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx else starti = 1; if ( assetid == zero ) return eval->Invalid("illegal assetid"); - else if ( AssetExactAmounts(cp,inputs,starti,outputs,eval,tx,assetid) == false ) + else if ( AssetExactAmounts(2, cp,inputs,starti,outputs,eval,tx,assetid) == false ) return eval->Invalid("asset inputs != outputs"); } + + switch ( funcid ) { case 'c': // create wont be called to be verified as it has no CC inputs @@ -321,7 +327,7 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx } fprintf(stderr,"fill validated\n"); break; - case 'E': // fillexchange + case 'E': // fillexchange return eval->Invalid("unexpected assets fillexchange funcid"); break; // disable asset swaps //vin.0: normal input @@ -333,7 +339,7 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx //vout.3: CC output for asset2 change (if any) //vout.3/4: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] - if ( AssetExactAmounts(cp,inputs,1,outputs,eval,tx,assetid2) == false ) + if ( AssetExactAmounts(1, cp,inputs,1,outputs,eval,tx,assetid2) == false ) eval->Invalid("asset2 inputs != outputs"); if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 ) return(false); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8f0a692b4..26f56b395 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5310,6 +5310,7 @@ int32_t ensure_CCrequirements() #include "../cc/CCOracles.h" #include "../cc/CCGateways.h" #include "../cc/CCPrices.h" +#include "../cc/CCHeir.h" UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vector &pubkey) { @@ -5531,15 +5532,25 @@ UniValue gatewaysaddress(const UniValue& params, bool fHelp) UniValue heiraddress(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; std::vector pubkey; + struct CCcontract_info *cp,C; std::vector destPubkey; + cp = CCinit(&C,EVAL_HEIR); - if ( fHelp || params.size() > 1 ) - throw runtime_error("heiraddress [pubkey]\n"); + if ( fHelp || (params.size() != 4 && params.size() != 3)) + throw runtime_error("heiraddress func txid amount [destpubkey]\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"); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp,(char *)"Heir",pubkey)); + //if ( params.size() == 1 ) + // pubkey = ParseHex(params[0].get_str().c_str()); + + char funcid = ((char *)params[0].get_str().c_str())[0]; + uint256 assetid = Parseuint256((char *)params[1].get_str().c_str()); + int64_t funds = atof(params[2].get_str().c_str()) * COIN ; + if(params.size() == 4) + destPubkey = ParseHex(params[3].get_str().c_str()); + + return HeirFundBad(funcid, assetid, funds, destPubkey); + + //return(CCaddress(cp,(char *)"Heir",pubkey)); } UniValue lottoaddress(const UniValue& params, bool fHelp) @@ -6837,7 +6848,7 @@ UniValue tokencreate(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); name = params[0].get_str(); - supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; + supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) if ( name.size() == 0 || name.size() > 32) { ERR_RESULT("Token name must not be empty and up to 32 characters"); @@ -6877,7 +6888,8 @@ UniValue tokentransfer(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); tokenid = Parseuint256((char *)params[0].get_str().c_str()); std::vector pubkey(ParseHex(params[1].get_str().c_str())); - amount = atol(params[2].get_str().c_str()); + //amount = atol(params[2].get_str().c_str()); + amount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance if ( tokenid == zeroid ) { ERR_RESULT("invalid tokenid"); @@ -6913,7 +6925,8 @@ UniValue tokenconvert(const UniValue& params, bool fHelp) evalcode = atoi(params[0].get_str().c_str()); tokenid = Parseuint256((char *)params[1].get_str().c_str()); std::vector pubkey(ParseHex(params[2].get_str().c_str())); - amount = atol(params[3].get_str().c_str()); + //amount = atol(params[3].get_str().c_str()); + amount = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance if ( tokenid == zeroid ) { ERR_RESULT("invalid tokenid"); @@ -6946,7 +6959,8 @@ UniValue tokenbid(const UniValue& params, bool fHelp) 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); - numtokens = atoi(params[0].get_str().c_str()); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance tokenid = Parseuint256((char *)params[1].get_str().c_str()); price = atof(params[2].get_str().c_str()); bidamount = (price * numtokens) * COIN + 0.0000000049999; @@ -7014,7 +7028,8 @@ UniValue tokenfillbid(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); tokenid = Parseuint256((char *)params[0].get_str().c_str()); bidtxid = Parseuint256((char *)params[1].get_str().c_str()); - fillamount = atol(params[2].get_str().c_str()); + // fillamount = atol(params[2].get_str().c_str()); + fillamount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance if ( fillamount <= 0 ) { ERR_RESULT("fillamount must be positive"); @@ -7043,10 +7058,12 @@ UniValue tokenask(const UniValue& params, bool fHelp) 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); - numtokens = atoi(params[0].get_str().c_str()); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance tokenid = Parseuint256((char *)params[1].get_str().c_str()); price = atof(params[2].get_str().c_str()); askamount = (price * numtokens) * COIN + 0.0000000049999; + //std::cerr << std::boolalpha << "tokenask(): (tokenid == zeroid) is " << (tokenid == zeroid) << " (numtokens <= 0) is " << (numtokens <= 0) << " (price <= 0) is " << (price <= 0) << " (askamount <= 0) is " << (askamount <= 0) << std::endl; if ( tokenid == zeroid || numtokens <= 0 || price <= 0 || askamount <= 0 ) { ERR_RESULT("invalid parameter"); @@ -7075,7 +7092,8 @@ UniValue tokenswapask(const UniValue& params, bool fHelp) 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); - numtokens = atoi(params[0].get_str().c_str()); + //numtokens = atoi(params[0].get_str().c_str()); + numtokens = atoll(params[0].get_str().c_str()); // dimxy changed to prevent loss of significance tokenid = Parseuint256((char *)params[1].get_str().c_str()); otherid = Parseuint256((char *)params[2].get_str().c_str()); price = atof(params[3].get_str().c_str()); @@ -7129,7 +7147,8 @@ UniValue tokenfillask(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); tokenid = Parseuint256((char *)params[0].get_str().c_str()); asktxid = Parseuint256((char *)params[1].get_str().c_str()); - fillunits = atol(params[2].get_str().c_str()); + //fillunits = atol(params[2].get_str().c_str()); + fillunits = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance if ( fillunits <= 0 ) { ERR_RESULT("fillunits must be positive"); @@ -7169,7 +7188,8 @@ UniValue tokenfillswap(const UniValue& params, bool fHelp) tokenid = Parseuint256((char *)params[0].get_str().c_str()); otherid = Parseuint256((char *)params[1].get_str().c_str()); asktxid = Parseuint256((char *)params[2].get_str().c_str()); - fillunits = atol(params[3].get_str().c_str()); + //fillunits = atol(params[3].get_str().c_str()); + fillunits = atoll(params[3].get_str().c_str()); // dimxy changed to prevent loss of significance hex = FillSell(0,tokenid,otherid,asktxid,fillunits); if (fillunits > 0) { if ( hex.size() > 0 ) {