diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index 52c73d47f..837c4361e 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -239,25 +239,28 @@ CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,st } */ -CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector origpubkey) +CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector voutPubkeys, std::vector origpubkey) { CScript opret; uint8_t evalcode = EVAL_ASSETS; uint8_t funcId = (uint8_t)'t'; + uint8_t ccType = 0; + if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) + ccType = voutPubkeys.size(); tokenid = revuint256(tokenid); switch ( assetFuncId ) { //case 't': this cannot be here case 'x': case 'o': - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << ccType; if(ccType >= 1) ss << voutPubkeys[0]; if(ccType == 2) ss << voutPubkeys[1]; ss << assetFuncId); break; case 's': case 'b': case 'S': case 'B': - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId << price << origpubkey); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << ccType; if(ccType >= 1) ss << voutPubkeys[0]; if(ccType == 2) ss << voutPubkeys[1]; ss << assetFuncId << price << origpubkey); break; case 'E': case 'e': assetid2 = revuint256(assetid2); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId << assetid2 << price << origpubkey); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << ccType; if(ccType >= 1) ss << voutPubkeys[0]; if(ccType == 2) ss << voutPubkeys[1]; ss << assetFuncId << assetid2 << price << origpubkey); break; default: fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId); @@ -283,33 +286,38 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &o uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) { - std::vector vopret; - uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyFuncId; + std::vector vopretExtra; + uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyAssetFuncId; uint256 dummyTokenid; - - GetOpReturnData(scriptPubKey, vopret); - - script = (uint8_t *)vopret.data(); - if (script == 0) { - std::cerr << "DecodeAssetOpRet() script is empty" << std::endl; - return (uint8_t)0; - } + std::vector voutPubkeysDummy; tokenid = zeroid; assetid2 = zeroid; price = 0; - bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! - bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof()); + // First - decode token opret: + funcId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra); - if (!result && isEof) { // NOTE: 'result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error' - std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << std::endl; + + /*GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if (script == 0) { + std::cerr << "DecodeAssetOpRet() script is empty" << std::endl; + return (uint8_t)0; + }*/ + //bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! + //bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof()); + + if (funcId == 0 || vopretExtra.size() == 0) { + std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretExtra.size()=" << vopretExtra.size() << std::endl; return (uint8_t)0; } - tokenid = revuint256(tokenid); + ////tokenid = revuint256(tokenid); already done in DecodeToken! - std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; + assetFuncId = vopretExtra.begin()[0]; + + //std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; if(evalCodeInOpret == EVAL_ASSETS) { @@ -328,20 +336,20 @@ uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, break; */ case 'x': case 'o': - if (isEof) // no data after 'assetFuncId' allowed + if (vopretExtra.size() == 1) // no data after 'assetFuncId' allowed { return(assetFuncId); } break; case 's': case 'b': case 'S': case 'B': - if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> dummyTokenid; ss >> dummyFuncId; ss >> price; ss >> origpubkey) != 0) + if (E_UNMARSHAL(vopretExtra, ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) { //fprintf(stderr,"got price %llu\n",(long long)price); return(assetFuncId); } break; case 'E': case 'e': - if ( E_UNMARSHAL(vopret,ss >> dummyEvalCode; ss >> dummyFuncId; ss >> dummyTokenid; ss >> dummyFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) + if ( E_UNMARSHAL(vopretExtra, ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) { //fprintf(stderr,"got price %llu\n",(long long)price); assetid2 = revuint256(assetid2); @@ -349,34 +357,37 @@ uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, } break; default: - fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n", funcId); - funcId = 0; + fprintf(stderr,"DecodeAssetOpRet: illegal assetFuncId.%02x\n", assetFuncId); + //funcId = 0; break; } } - return(funcId); + return (uint8_t)0; } bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx) { uint256 assetid,assetid2; uint8_t evalCode; - if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode,assetid,assetid2,price,origpubkey) != 0 ) + if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) return(true); - else return(false); + else + return(false); } bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,const CTransaction& tx) { - uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector origpubkey; CScript script; + uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector origpubkey; + CScript script; uint8_t evalCode; n = tx.vout.size(); if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey, evalCode,assetid,assetid2,price,origpubkey)) == 0 ) return(false); - if ( GetCCaddress(cp,CCaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 ) + if ( GetCCaddress(cp, CCaddr, pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr, CScript() << origpubkey << OP_CHECKSIG) != 0 ) return(true); - else return(false); + else + return(false); } @@ -398,7 +409,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch } else if ( Getscriptaddress(destaddr,vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || strcmp(destaddr,(char *)cp->unspendableCCaddr) != 0 ) { - fprintf(stderr,"%s vs %s\n",destaddr,(char *)cp->unspendableCCaddr); + fprintf(stderr,"AssetValidateCCvin cc addr %s is not evalcode 0x%02x unspendable %s\n", destaddr, (int)cp->evalcode, (char *)cp->unspendableCCaddr); return eval->Invalid("invalid vin AssetsCCaddr"); } //else if ( vinTx.vout[0].nValue < 10000 ) @@ -415,6 +426,8 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr { CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; CCaddr[0] = origaddr[0] = 0; + + // validate locked coins on Assets vin[1] if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 ) return(0); else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) @@ -441,7 +454,8 @@ int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpp return(0); if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey,vinTx,0,assetid)) == 0 ) return eval->Invalid("invalid missing CC vout0 for sellvin"); - else return(assetoshis); + else + return(assetoshis); } @@ -475,9 +489,8 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr return(true); } } */ - // TODO: hope this was unneeded!!! (dimxy) - else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset - return(false); + //else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset + // return(false); else if (funcid != 'E') { if (assetid != zeroid && assetidOpret == assetid) @@ -542,8 +555,8 @@ bool AssetExactAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_ cpTokens = CCinit(&C, EVAL_TOKENS); for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) || (*cpTokens->ismyvin)(tx.vin[i].scriptSig)) // || IsVinAllowed(tx.vin[i].scriptSig) != 0) + { // only tokens are relevant!! + if (/*(*cpAssets->ismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0) { //std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; // we are not inside the validation code -- dimxy @@ -555,7 +568,10 @@ bool AssetExactAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_ else { // validate vouts of vintx //std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; - assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid); + //assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid); + std::vector vopretExtra; + std::vector vinPubkeysEmpty; + assetoshis = IsTokensvout(false, false, cpTokens, NULL, vopretExtra, vinTx, tx.vin[i].prevout.n, assetid, vinPubkeysEmpty); if (assetoshis != 0) { std::cerr << "AssetExactAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl; @@ -585,9 +601,11 @@ bool AssetExactAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_ } } - //std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; + std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - /* if (inputs != outputs) { + /* we do not verify inputs == outputs here, + it's done in Tokens: + if (inputs != outputs) { if (tx.GetHash() != assetid) { std::cerr << "AssetExactAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs"); diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 17c16bd6a..fe86d58de 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -145,11 +145,11 @@ UniValue AssetOrders(uint256 refassetid) char numstr[32], funcidstr[16], origaddr[64], assetidstr[65]; txid = it->first.txhash; - //std::cerr << "addOrders txid" << txid.GetHex() << std::endl; + //std::cerr << "addOrders() txid=" << txid.GetHex() << std::endl; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); - //std::cerr << "addOrders vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << std::endl; + //std::cerr << "addOrders() vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl; if (vintx.vout.size() > 0 && (funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) { if (refassetid != zero && assetid != refassetid) @@ -163,6 +163,8 @@ UniValue AssetOrders(uint256 refassetid) //fprintf(stderr," refassetid\n"); return; } + + //std::cerr << "addOrders() it->first.index=" << it->first.index << " vintx.vout[it->first.index].nValue=" << vintx.vout[it->first.index].nValue << std::endl; if (vintx.vout[it->first.index].nValue == 0) return; @@ -213,7 +215,7 @@ UniValue AssetOrders(uint256 refassetid) } } result.push_back(item); - //fprintf(stderr,"func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN); + //fprintf(stderr,"addOrders() func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN); } } }; @@ -378,8 +380,12 @@ std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, in return (""); } - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, GetUnspendable(cpAssets,0))); - return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, EncodeAssetOpRet('b', assetid, zeroid, pricetotal, Mypubkey()))); + CPubKey unspendablePubkey = GetUnspendable(cpAssets, 0); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendablePubkey)); + + std::vector voutTokenPubkeys; // should be empty - no token vouts + + return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, EncodeAssetOpRet('b', assetid, zeroid, pricetotal, voutTokenPubkeys, Mypubkey()))); } CCerror = strprintf("no coins found to make buy offer"); return(""); @@ -420,13 +426,17 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p return (""); } - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,askamount, GetUnspendable(cpTokens,0))); + CPubKey unspendablePubkey = GetUnspendable(cpTokens, 0); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, unspendablePubkey)); if (inputs > askamount) CCchange = (inputs - askamount); if (CCchange != 0) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); - opret = EncodeAssetOpRet('s',assetid, zeroid, pricetotal, Mypubkey()); + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(unspendablePubkey); + + opret = EncodeAssetOpRet('s',assetid, zeroid, pricetotal, voutTokenPubkeys, Mypubkey()); return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee,opret)); } else { @@ -474,18 +484,22 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a return (""); } - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, GetUnspendable(cp, 0))); + CPubKey unspendablePubkey = GetUnspendable(cp, 0); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, unspendablePubkey)); if (inputs > askamount) CCchange = (inputs - askamount); if (CCchange != 0) mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); - if (assetid2 == zeroid) - opret = EncodeAssetOpRet('s', assetid, zeroid, pricetotal, Mypubkey()); - else - { - opret = EncodeAssetOpRet('e', assetid, assetid2, pricetotal, Mypubkey()); + + std::vector voutTokenPubkeys; // should be empty - no token vouts + + if (assetid2 == zeroid) { + opret = EncodeAssetOpRet('s', assetid, zeroid, pricetotal, voutTokenPubkeys, Mypubkey()); + } + else { + opret = EncodeAssetOpRet('e', assetid, assetid2, pricetotal, voutTokenPubkeys, Mypubkey()); } return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); } @@ -524,9 +538,12 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) { bidamount = vintx.vout[0].nValue; - mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); + mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, EncodeAssetOpRet('o', assetid, zeroid, 0, Mypubkey()))); + + std::vector voutTokenPubkeys; // should be empty, no tokens vout + + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, EncodeAssetOpRet('o', assetid, zeroid, 0, voutTokenPubkeys, Mypubkey()))); } } return(""); @@ -554,7 +571,11 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) askamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk)); - return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, EncodeAssetOpRet('x', assetid, zeroid, 0, Mypubkey()))); + + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(mypk); + + return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, EncodeAssetOpRet('x', assetid, zeroid, 0, voutTokenPubkeys, Mypubkey()))); } } return(""); @@ -595,7 +616,7 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f bidamount = vintx.vout[bidvout].nValue; SetAssetOrigpubkey(origpubkey, origprice, vintx); - mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); + mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60)) > 0) { @@ -610,25 +631,30 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f if (inputs > fillamount) CCchange = (inputs - fillamount); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, bidamount - paid_amount, GetUnspendable(cpTokens, NULL))); // tokens - mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, fillamount, pubkey2pk(origpubkey))); // coins on assets + CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL); + uint8_t unspendableAssetsPrivkey[32]; + CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); + + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // 0 coins remainder + mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // 1 coins to normal + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, fillamount, pubkey2pk(origpubkey))); // 2 tokens paid if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); // coins on assets + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // 3 change in tokens fprintf(stderr,"remaining %llu -> origpubkey\n", (long long)remaining_required); - // add additional unspendable addr from Assets: char unspendableAssetsAddr[64]; - uint8_t unspendableAssetsPrivkey[32]; - - CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey); GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk); + // add additional unspendable addr from Assets: CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); - return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee, EncodeAssetOpRet('B', assetid, zeroid, remaining_required, origpubkey))); - } else return("dont have any assets to fill bid\n"); + // token vout verification pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(unspendableTokensPk); + + return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee, EncodeAssetOpRet('B', assetid, zeroid, remaining_required, voutTokenPubkeys, origpubkey))); + } else return("dont have any assets to fill bid"); } } return("no normal coins left"); @@ -683,7 +709,7 @@ std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 askt mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); // NOTE: this is the reference to tokens -> send cpTokens for signing into FinalizeCCTx! if (assetid2 != zeroid) - inputs = AddAssetInputs(cpTokens, mtx, mypk, assetid2, paid_nValue, 60); + inputs = AddAssetInputs(cpTokens, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet else { inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60); @@ -698,25 +724,36 @@ std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 askt } if (assetid2 != zeroid) - SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); + SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet else SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); if (assetid2 != zeroid && inputs > paid_nValue) CCchange = (inputs - paid_nValue); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // coins in Assets cc addr - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); // tokens + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, orig_assetoshis - received_assetoshis, GetUnspendable(cpTokens, NULL))); // 0 tokens cc addr - ask remainder + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); //1 tokens to self - if (assetid2 != zeroid) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); // tokens... (swap is not implemented yet) - else - mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); // coins normal + if (assetid2 != zeroid) { + std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //2 tokens... (swap is not implemented yet) + } + else { + //std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl; + mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //2 coins normal to whom who asked token + } - if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); // coins in Assets cc addr + // not implemented + if (CCchange != 0) { + std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl; + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //3 coins in Assets cc addr + } - return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee,EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid, assetid2, remaining_nValue, origpubkey))); + // vout verification pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(mypk); + + return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee,EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid, assetid2, remaining_nValue, voutTokenPubkeys, origpubkey))); } else { CCerror = strprintf("filltx not enough utxos"); fprintf(stderr,"%s\n", CCerror.c_str()); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index ec1557e2b..1b6e804fc 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -151,13 +151,15 @@ uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk); //int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs); int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector vinPubkeys); + bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); -CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector origpubkey); +CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector voutPubkeys, std::vector origpubkey); bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &vopretExtra); +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra); //uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector &origpubkey); uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index c85297ddb..d9e689f85 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -76,11 +76,12 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector return (uint8_t)0; } -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &vopretExtra) +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { std::vector vopret, extra, dummyPubkey; - uint8_t funcid=0, *script, e, dummyFuncId; + uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; std::string dummyName; std::string dummyDescription; + CPubKey voutPubkey1, voutPubkey2; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); @@ -88,28 +89,45 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 if (script != 0 /*enable all evals: && script[0] == EVAL_TOKENS*/) { + // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! bool isEof = true; + evalCode = script[0]; - funcid = script[1]; - //fprintf(stderr,"decode.[%c]\n",funcid); - switch ( funcid ) + funcId = script[1]; + //fprintf(stderr,"decode.[%c]\n",funcId); + + switch ( funcId ) { case 'c': return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription); //break; case 't': //not used yet: case 'l': - if (E_UNMARSHAL(vopret, ss >> e; ss >> dummyFuncId; ss >> tokenid; isEof = ss.eof(); vopretExtra = std::vector(ss.begin(), ss.end())) || !isEof) + // NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error' + if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; if (ccType >= 1) ss >> voutPubkey1; if (ccType == 2) ss >> voutPubkey2; isEof = ss.eof(); vopretExtra = std::vector(ss.begin(), ss.end())) + || !isEof) { + + if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType + std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << std::endl; + return (uint8_t)0; + } + + // add verification pubkeys: + voutPubkeys.clear(); + if (voutPubkey1.IsValid()) + voutPubkeys.push_back(voutPubkey1); + if (voutPubkey2.IsValid()) + voutPubkeys.push_back(voutPubkey2); + tokenid = revuint256(tokenid); - return(funcid); + return(funcId); } - std::cerr << "DecodeTokenOpRet() isEof=" << isEof << std::endl; - fprintf(stderr, "DecodeTokenOpRet() bad opret format\n"); // this may be just check, no error logging + std::cerr << "DecodeTokenOpRet() warning: bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; return (uint8_t)0; default: - fprintf(stderr, "DecodeTokenOpRet() illegal funcid.%02x\n", funcid); + std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl; return (uint8_t)0; } } @@ -124,16 +142,18 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & static uint256 zero; CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; - int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; std::vector origpubkey, tmporigpubkey, ignorepubkey; + int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; + std::vector vopretExtra, tmporigpubkey, ignorepubkey; uint8_t funcid, evalCodeInOpret; char destaddr[64], origaddr[64], CCaddr[64]; + std::vector voutTokenPubkeys; numvins = tx.vin.size(); numvouts = tx.vout.size(); outputs = inputs = 0; preventCCvins = preventCCvouts = -1; - if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, origpubkey)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0) return eval->Invalid("TokenValidate: invalid opreturn payload"); fprintf(stderr, "TokensValidate (%c)\n", funcid); @@ -206,7 +226,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & thread_local uint32_t tokenValIndentSize = 0; // validates opret for token tx: -bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector &vopretExtra) { +bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { uint256 tokenidOpret, tokenidOpret2; uint8_t funcid, evalCode; @@ -216,7 +236,7 @@ bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector int32_t n = tx.vout.size(); - if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, tokenidOpret, vopretExtra)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) { std::cerr << indentStr << "ValidateTokenOpret() DecodeOpret returned null for n-1=" << n - 1 << " txid=" << tx.GetHash().GetHex() << std::endl; return(false); @@ -241,18 +261,20 @@ bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector } + // Checks if the vout is a really Tokens CC vout // compareTotals == true, the func also validates the passed transaction itself: // it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx -int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, std::vector &vopretExtra, const CTransaction& tx, int32_t v, uint256 reftokenid) +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector &vopretExtra, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector vinPubkeys) { // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); //std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl; + //TODO: validate cc vouts are EVAL_TOKENS! - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here { int32_t n = tx.vout.size(); // just check boundaries: @@ -261,7 +283,7 @@ int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, return(0); } - if (compareTotals) { + if (goDeeper) { //std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl; //validate all tx int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; @@ -282,11 +304,41 @@ int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, } // moved opret checking to this new reusable func (dimxy): - const bool valOpret = ValidateTokenOpret(tx, v, reftokenid, vopretExtra); + std::vector voutPubkeys; + const bool valOpret = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra); //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; if (valOpret) { - //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; + //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned true" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + + if (checkPubkeys) { + // verify that the vout is within EVAL_TOKENS: + + if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { + CTxOut testVout; + if (voutPubkeys.size() == 1) + testVout = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]); + else // voutPubkeys.size() == 2 + testVout = MakeCC1of2vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]); + + if (tx.vout[v].scriptPubKey == testVout.scriptPubKey) { + std::cerr << indentStr << "IsTokensvout() vout is EVAL_TOKENS, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + return tx.vout[v].nValue; + } + } + + // maybe it is change? + for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { + CTxOut testVout = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it); + + if (tx.vout[v].scriptPubKey == testVout.scriptPubKey) { + std::cerr << indentStr << "IsTokensvout() vout is EVAL_TOKENS change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + return tx.vout[v].nValue; + } + } + } + else { + return tx.vout[v].nValue; + } } //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); @@ -296,9 +348,12 @@ int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, } // compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) -bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) { CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t tokenoshis; std::vector tmporigpubkey; int64_t tmpprice; + std::vector vinPubkeys, vinPubkeysEmpty; + CPubKey pubkey; + int32_t numvins = tx.vin.size(); int32_t numvouts = tx.vout.size(); inputs = outputs = 0; @@ -310,6 +365,33 @@ bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, in { // check for additional contracts which may send tokens to the Tokens contract if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) { + + // extract my vins pubkeys: + + auto findEval = [](CC *cond, struct CCVisitor _) { + bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS; + + if (cc_typeId(cond) == CC_Secp256k1) { + *(CPubKey*)_.context = buf2pk(cond->publicKey); + std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl; + r = true; + } + // false for a match, true for continue + return r ? 0 : 1; + }; + + CC *cond = GetCryptoCondition(tx.vin[i].scriptSig); + + if (cond) { + CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey }; + bool out = !cc_visit(cond, visitor); + cc_free(cond); + + if (pubkey.IsValid()) + vinPubkeys.push_back(pubkey); + } + + //std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl; // we are not inside the validation code -- dimxy if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) @@ -322,7 +404,7 @@ bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, in tokenValIndentSize++; // validate vouts of vintx //std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; - tokenoshis = IsTokensvout(compareTotals, cpTokens, eval, tmporigpubkey, vinTx, tx.vin[i].prevout.n, tokenid); + tokenoshis = IsTokensvout(goDeeper, false/*<--do not have pubkeys*/, cpTokens, eval, tmporigpubkey, vinTx, tx.vin[i].prevout.n, tokenid, vinPubkeysEmpty); tokenValIndentSize--; if (tokenoshis != 0) { @@ -339,7 +421,7 @@ bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, in tokenValIndentSize++; // Note: we pass in here 'false' because we don't need to call TokenExactAmounts() recursively from IsTokenvout // indeed, in this case we'll be checking this tx again - tokenoshis = IsTokensvout(false, cpTokens, eval, tmporigpubkey, tx, i, tokenid); + tokenoshis = IsTokensvout(false, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tmporigpubkey, tx, i, tokenid, vinPubkeys); tokenValIndentSize--; if (tokenoshis != 0) @@ -393,7 +475,9 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C if (strcmp(destaddr, coinaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0) continue; fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN); - if ((nValue = IsTokensvout(true, cp, NULL, vopretExtra, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) + + std::vector vinPubkeysEmpty; + if ((nValue = IsTokensvout(true, false, cp, NULL, vopretExtra, vintx, vout, tokenid, vinPubkeysEmpty)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) { if (total != 0 && maxinputs != 0) mtx.vin.push_back(CTxIn(txid, vout, CScript())); diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index 2dc9a8e56..8ecbc1094 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -27,8 +27,8 @@ // CCcustom bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); -int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid); +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); +//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector vinPubkeys); std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description); std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index dc8b4788e..3d95d0f17 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -333,7 +333,8 @@ int64_t CCtoken_balance(char *coinaddr,uint256 tokenid) if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { char str[65]; fprintf(stderr,"check %s %.8f\n",uint256_str(str,txid),(double)it->second.satoshis/COIN); - if ( DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, assetid, vopretExtra) != 0 && assetid == tokenid ) + std::vector voutTokenPubkeys; + if ( DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, assetid, voutTokenPubkeys, vopretExtra) != 0 && assetid == tokenid ) { sum += it->second.satoshis; } diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index c0fea0c9f..eef21c72a 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -307,17 +307,17 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK return(destaddr[0] != 0); } -bool ConstrainVout(CTxOut vout,int32_t CCflag,char *cmpaddr,int64_t nValue) +bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue) { char destaddr[64]; if ( vout.scriptPubKey.IsPayToCryptoCondition() != CCflag ) { - fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n",vout.scriptPubKey.IsPayToCryptoCondition(),CCflag); + fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n", vout.scriptPubKey.IsPayToCryptoCondition(), CCflag); return(false); } - else if ( cmpaddr != 0 && (Getscriptaddress(destaddr,vout.scriptPubKey) == 0 || strcmp(destaddr,cmpaddr) != 0) ) + else if ( cmpaddr != 0 && (Getscriptaddress(destaddr, vout.scriptPubKey) == 0 || strcmp(destaddr, cmpaddr) != 0) ) { - fprintf(stderr,"constrain vout error addr %s vs %s\n",cmpaddr!=0?cmpaddr:"",destaddr!=0?destaddr:""); + fprintf(stderr,"constrain vout error addr %s vs %s\n", cmpaddr!=0?cmpaddr:"", destaddr!=0?destaddr:""); return(false); } else if ( nValue != 0 && nValue != vout.nValue ) //(nValue == 0 && vout.nValue < 10000) || ( diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 47b9488c8..3a62a526a 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -140,12 +140,16 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector origpubkey,tmporigpubkey,ignorepubkey; uint8_t funcid, evalCodeInOpret; - char destaddr[64],origaddr[64],CCaddr[64]; + char destaddr[64], origaddr[64], assetsCCaddr[64], tokensCCaddr[64]; // we need this for validating tokens' vins/vous: struct CCcontract_info *cpTokens, tokensC; cpTokens = CCinit(&tokensC, EVAL_TOKENS); + //CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL); + //CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, NULL); + //GetCCaddress(cpTokens, tokensUnspendableCCaddr, unspendableTokensPk); + numvins = tx.vin.size(); numvouts = tx.vout.size(); outputs = inputs = 0; @@ -154,6 +158,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if((funcid = DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); + // find token user cc addr + GetCCaddress(cpTokens, tokensCCaddr, pubkey2pk(origpubkey)); + fprintf(stderr,"AssetValidate (%c)\n",funcid); if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 ) @@ -162,8 +169,8 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("cant find asset2 create txid"); else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) return eval->Invalid("illegal asset vin0"); - else if( numvouts < 1 ) - return eval->Invalid("no vouts"); + else if( numvouts < 2 ) + return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below else if( funcid != 'c' ) { /* if( funcid == 't' ) @@ -223,7 +230,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey] //vout.1: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['o'] - if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 ) + if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) return(false); else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 ) return eval->Invalid("invalid refund for cancelbuy"); @@ -243,7 +250,8 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.4: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] preventCCvouts = 4; - if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 ) + + if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillbuy"); @@ -255,12 +263,12 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if( ConstrainVout(tx.vout[2], 1, CCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[2], 1, tokensCCaddr, 0) == 0 ) // tokens on user cc addr return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); else if ( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); } - else if( ConstrainVout(tx.vout[2], 1, CCaddr, inputs) == 0 ) + else if( ConstrainVout(tx.vout[2], 1, tokensCCaddr, inputs) == 0 ) // tokens on user cc addr return eval->Invalid("vout2 doesnt match inputs fillbuy"); else if( ConstrainVout(tx.vout[1],0,0,0) == 0 ) return eval->Invalid("vout1 is CC for fillbuy"); @@ -268,7 +276,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("mismatched remainder for fillbuy"); else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins on asset unspendable cc addr return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); } } @@ -287,16 +295,16 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti preventCCvouts = 1; if( remaining_price == 0 ) return eval->Invalid("illegal null remaining_price for selloffer"); - if( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 ) + if( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 ) // is cc change present? { preventCCvouts++; - if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) - return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer"); + if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // check also vout[0] + return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); else if( tx.vout[0].nValue + tx.vout[1].nValue != inputs ) return eval->Invalid("mismatched vout0+vout1 total for selloffer"); } - else if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, inputs) == 0 ) - return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer"); + else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // no cc change, just vout[0] + return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); break; @@ -306,9 +314,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey] //vout.1: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - if( (assetoshis= AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis= AssetValidateSellvin(cpTokens, eval, tmpprice, tmporigpubkey, tokensCCaddr, origaddr, tx, assetid)) == 0 ) return(false); - else if( ConstrainVout(tx.vout[0], 1, CCaddr, assetoshis) == 0 ) + else if( ConstrainVout(tx.vout[0], 1, tokensCCaddr, assetoshis) == 0 ) return eval->Invalid("invalid vout for cancel"); preventCCvins = 2; preventCCvouts = 1; @@ -323,7 +331,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //'S'.vout.2: vin.2 value to original pubkey [origpubkey] //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - if( (assetoshis = AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis = AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, tokensCCaddr, origaddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillask"); @@ -341,8 +349,8 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("normal vout1 for fillask"); else if( remaining_price != 0 ) { - if ( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr,0) == 0 ) - return eval->Invalid("mismatched vout0 AssetsCCaddr for fill"); + if ( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr,0) == 0 ) + return eval->Invalid("mismatched vout0 TokenCCaddr for fill"); } } fprintf(stderr,"fill validated\n"); @@ -363,7 +371,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false ) // eval->Invalid("asset2 inputs != outputs"); - if( (assetoshis= AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, tokensCCaddr, origaddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillex"); @@ -375,7 +383,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("locked value doesnt match vout0+1 fillex"); else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if( ConstrainVout(tx.vout[2], 1, CCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[2], 1, tokensCCaddr, 0) == 0 ) return eval->Invalid("vout2 doesnt go to origpubkey fillex"); else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) { @@ -383,7 +391,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("asset inputs doesnt match vout2+3 fillex"); } } - else if( ConstrainVout(tx.vout[2], 1, CCaddr, inputs) == 0 ) + else if( ConstrainVout(tx.vout[2], 1, tokensCCaddr, inputs) == 0 ) return eval->Invalid("vout2 doesnt match inputs fillex"); else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) return eval->Invalid("vout1 is CC for fillex"); @@ -394,7 +402,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("normal vout1 for fillex"); else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex"); } } diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 5d919d6e8..9ba6d4a84 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -326,7 +326,9 @@ std::string PricesAddFunding(uint64_t txfee,uint256 refbettoken,uint256 fundingt CCchange = (inputs - amount); mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); // add addr2 - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',bettoken,zeroid,0,Mypubkey()))); + + std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',bettoken,zeroid,0, voutTokenPubkeysEmpty, Mypubkey()))); } else { diff --git a/src/script/script.cpp b/src/script/script.cpp index 475acdfd5..ba3c7f78f 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -407,6 +407,20 @@ bool CScript::MayAcceptCryptoCondition() const cc_free(cond); return out; } +struct CC *CScript::GetCryptoCondition() const +{ + // Get the type mask of the condition + const_iterator pc = this->begin(); + vector data; + opcodetype opcode; + if (!this->GetOp(pc, opcode, data)) return NULL; + if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return NULL; + struct CC *cond = cc_readConditionBinary(data.data(), data.size()); + if (!cond) return NULL; + //bool out = IsSupportedCryptoCondition(cond); + //cc_free(cond); + return cond; +} bool CScript::IsCoinImport() const { diff --git a/src/script/script.h b/src/script/script.h index b1294ab25..68757ef12 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -601,6 +601,8 @@ public: bool IsPayToCryptoCondition() const; bool IsCoinImport() const; bool MayAcceptCryptoCondition() const; + struct CC *GetCryptoCondition() const; + /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ bool IsPushOnly() const;