//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()<2){
std::cerr<<"DecodeAssetOpRet() incorrect opret or no asset's payload"<<" funcId="<<(int)funcId<<" vopretExtra.size()="<<vopretExtra.size()<<std::endl;
if(funcId==0||vopretAssets.size()<2){
std::cerr<<"DecodeAssetTokenOpRet() incorrect opret or no asset's payload"<<" funcId="<<(int)funcId<<" vopretAssets.size()="<<vopretAssets.size()<<std::endl;
fprintf(stderr,"AssetValidateCCvin cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n",destaddr,(int)cp->evalcode,unspendableAddr);
returneval->Invalid("invalid vin AssetsCCaddr");
fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n",destaddr,(int)cp->evalcode,unspendableAddr);
returneval->Invalid("invalid vin assets CCaddr");
}
// if fillBuy or cancelBuy --> to spend coins from asset unspendable addr
// if fillBuy or cancelBuy --> should spend coins from asset unspendable addr
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
int32_tn=tx.vout.size();
// just check boundaries:
if(v>=n-1){// just moved this up (dimxy)
std::cerr<<"isAssetVout() internal err: (v >= n - 1), returning 0"<<std::endl;
return(0);
}
if(tx.vout[v].scriptPubKey.IsPayToCryptoCondition()!=0)// maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_tn=tx.vout.size();
// just check boundaries:
if(v>=n-1){// just moved this up (dimxy)
std::cerr<<"isAssetVout() internal err: (v >= n - 1), returning 0"<<std::endl;
return(0);
}
// moved opret checking to this new reusable func (dimxy):
EncodeTokenOpRet(assetid,voutTokenPubkeys,// TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret.
EncodeAssetOpRet('b',zeroid,pricetotal,Mypubkey()))));// But still such token opret should not make problems because no token eval in these vouts
}
CCerror=strprintf("no coins found to make buy offer");
return("");
@ -348,7 +352,6 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
CPubKeymypk;
uint64_tmask;
int64_tinputs,CCchange;
CScriptopret;
structCCcontract_info*cpAssets,assetsC;
structCCcontract_info*cpTokens,tokensC;
@ -359,7 +362,8 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
return("");
}
cpAssets=CCinit(&assetsC,EVAL_ASSETS);// NOTE: this is for signing
cpAssets=CCinit(&assetsC,EVAL_ASSETS);// NOTE: for signing
if(txfee==0)
txfee=10000;
@ -367,10 +371,11 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
mypk=pubkey2pk(Mypubkey());
if(AddNormalinputs(mtx,mypk,2*txfee,3)>0)
{
std::vector<uint8_t>vopretNonfungible;
mask=~((1LL<<mtx.vin.size())-1);
// add single-eval tokens:
cpTokens=CCinit(&tokensC,EVAL_TOKENS);// NOTE: tokens is here
//CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress
mtx.vout.push_back(CTxOut(paid_amount,CScript()<<ParseHex(HexStr(mypk))<<OP_CHECKSIG));// vout1 coins to normal
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,fillamount,pubkey2pk(origpubkey)));// vout2 single-eval tokens sent to the buyer
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey));// vout3 marker to origpubkey
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2==0?EVAL_TOKENS:additionalTokensEvalcode2,fillamount,pubkey2pk(origpubkey)));// vout2 single-eval tokens sent to the originator
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey));// vout3 marker to origpubkey
if(CCchange!=0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,CCchange,mypk));// vout4 change in single-eval tokens
inputs=AddNormalinputs(mtx,mypk,2*txfee+paid_nValue,60);// Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS,orig_assetoshis-received_assetoshis,GetUnspendable(cpAssets,NULL)));// vout.0 tokens remainder to unspendable cc addr
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,received_assetoshis,mypk));//vout.1 purchased tokens to self single-eval addr
// vout.0 tokens remainder to unspendable cc addr:
std::cerr<<"DecodeTokenOpRet() bad opret format, isEof="<<isEof<<" ccType="<<ccType<<" tokenid="<<revuint256(tokenid).GetHex()<<std::endl;
LOGSTREAM("tokens",CCLOG_INFO,stream<<"DecodeTokenOpRet() bad opret format, isEof="<<isEof<<" ccType="<<ccType<<" tokenid="<<revuint256(tokenid).GetHex()<<std::endl);
std::cerr<<indentStr<<"ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid="<<tx.GetHash().GetHex()<<std::endl;
return(false);
LOGSTREAM("tokens",CCLOG_INFO,stream<<indentStr<<"ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid="<<tx.GetHash().GetHex()<<std::endl);
return(uint8_t)0;
}
elseif(funcid=='c')
{
if(tokenid!=zeroid&&tokenid==tx.GetHash()&&v==0){
//std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
if(tokenid!=zeroid&&tokenid==tx.GetHash()){
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"ValidateTokenOpret() this is the tokenbase 'c' tx, txid="<<tx.GetHash().GetHex()<<" returning true"<<std::endl);
returnfuncid;
}
else{
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"ValidateTokenOpret() not my tokenbase txid="<<tx.GetHash().GetHex()<<std::endl);
//std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"ValidateTokenOpret() this is a transfer 't' tx, txid="<<tx.GetHash().GetHex()<<" returning true"<<std::endl);
returnfuncid;
}
else{
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"ValidateTokenOpret() not my tokenid="<<tokenidOpret.GetHex()<<std::endl);
//std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl;
LOGSTREAM("tokens",CCLOG_DEBUG2,stream<<indentStr<<"IsTokensvout() entered for txid="<<tx.GetHash().GetHex()<<" v="<<v<<" for tokenid="<<reftokenid.GetHex()<<std::endl);
int32_tn=tx.vout.size();
// just check boundaries:
if(n==0||v<0||v>=n-1){
LOGSTREAM("tokens",CCLOG_INFO,stream<<indentStr<<"isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)"<<" v="<<v<<" n="<<n<<" returning 0"<<std::endl);
return(0);
}
//TODO: validate cc vouts are EVAL_TOKENS!
if(tx.vout[v].scriptPubKey.IsPayToCryptoCondition())// maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_tn=tx.vout.size();
// just check boundaries:
if(v>=n-1){// just moved this up (dimxy)
std::cerr<<indentStr<<"isTokensvout() internal err: (v >= n - 1), returning 0"<<std::endl;
// if ccInputs != ccOutputs and it is not the tokenbase tx
// this means it is possibly a fake tx (dimxy):
if(reftokenid!=tx.GetHash()){// checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr<<indentStr<<"IsTokensvout() warning: for the verified tx detected a bad vintx="<<tx.GetHash().GetHex()<<": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx"<<std::endl;
LOGSTREAM("tokens",CCLOG_INFO,stream<<indentStr<<"IsTokensvout() warning: for the verified tx detected a bad vintx="<<tx.GetHash().GetHex()<<": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx"<<std::endl);
return0;
}
}
}
// moved opret checking to this new reusable func (dimxy):
if(vopretExtra.size()>=2/*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/){
evalCodeInOpret=vopretExtra.begin()[1];
}
else{
// if payload is empty maybe it is a claim to non-payload-one-token-eval vout?
evalCodeInOpret=EVAL_TOKENS;
}
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"IsTokensvout() ValidateTokenOpret returned not-null funcId="<<(char)(funcId?funcId:'')<<" for txid="<<tx.GetHash().GetHex()<<" for tokenid="<<reftokenid.GetHex()<<std::endl);
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"IsTokensvout() no valid vouts evalCode="<<(int)evalCode<<" evalCode2="<<(int)evalCode2<<" for txid="<<tx.GetHash().GetHex()<<" for tokenid="<<reftokenid.GetHex()<<std::endl);
}
else{
//std::cerr << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
LOGSTREAM("tokens",CCLOG_INFO,stream<<indentStr<<"IsTokensvout() returns without pubkey check value="<<tx.vout[v].nValue<<" for txid="<<tx.GetHash().GetHex()<<" for tokenid="<<reftokenid.GetHex()<<std::endl);
LOGSTREAM("tokens",CCLOG_DEBUG2,stream<<indentStr<<"TokensExactAmounts() entered for txid="<<tx.GetHash().GetHex()<<" for tokenid="<<reftokenid.GetHex()<<std::endl);
for(int32_ti=0;i<numvins;i++)
{// check for additional contracts which may send tokens to the Tokens contract
std::cerr<<indentStr<<"TokenExactAmounts() found unequal token cc inputs="<<inputs<<" vs cc outputs="<<outputs<<" for txid="<<tx.GetHash().GetHex()<<" and this is not the create tx"<<std::endl;
if(tx.GetHash()!=reftokenid)
LOGSTREAM("tokens",CCLOG_DEBUG1,stream<<indentStr<<"TokenExactAmounts() found unequal token cc inputs="<<inputs<<" vs cc outputs="<<outputs<<" for txid="<<tx.GetHash().GetHex()<<" and this is not the create tx"<<std::endl);
threshold=total/(maxinputs!=0?maxinputs:64);// TODO: is maxinputs really could not be over 64? what if i want to calc total balance?
if(unspentOutputs.empty()){
LOGSTREAM("tokens",CCLOG_INFO,stream<<"AddTokenCCInputs() no utxos for token dual/three eval addr="<<tokenaddr<<" evalcode="<<(int)cp->evalcode<<" additionalTokensEvalcode2="<<(int)cp->additionalTokensEvalcode2<<std::endl);
}
threshold=total/(maxinputs!=0?maxinputs:64);// TODO: maxinputs really could not be over 64? what if i want to calc total balance for all available uxtos?
// maybe it is better to add all uxtos if maxinputs == 0
mask=~((1LL<<mtx.vin.size())-1);// seems, mask is not used anymore
if((inputs=AddTokenCCInputs(cp,mtx,mypk,tokenid,total,60,vopretNonfungible))>0)// NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx!
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,total,pubkey2pk(destpubkey)));// TODO: or MakeTokensCC1vout??
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode,total,pubkey2pk(destpubkey)));// if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...)
voutTokenPubkeys.push_back(pubkey2pk(destpubkey));// dest pubkey for validating vout
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeTokenOpRet('t',EVAL_TOKENS,assetid,voutTokenPubkeys,CScript())));// By setting EVAL_TOKENS we're getting out from assets validation code
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end
//This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation.
if((payoutCond=MakeTokensCCcond1of2(cp->evalcode,cp->additionalTokensEvalcode2,pk,pk2))!=0)// if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc
elseif(!AssetCalcAmounts(cpAssets,inputs,outputs,eval,tx,assetid)){// Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
elseif(!AssetCalcAmounts(cpAssets,inputs,outputsDummy/*outputsDummy is calculated incorrectly but not used*/,eval,tx,assetid)){// Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
returnfalse;// returns false if some problems with reading vintxes
returneval->Invalid("invalid normal vout1 for sellvin");
if(tx.vout[2].scriptPubKey.IsPayToCryptoCondition()!=0)// cc change
if(tx.vout[2].scriptPubKey.IsPayToCryptoCondition()!=0)// if cc change presents
{
preventCCvouts++;
if(ConstrainVout(tx.vout[0],1,(char*)cpTokens->unspendableCCaddr,0)==0)// check also cc vout[0]
if(ConstrainVout(tx.vout[0],1,(char*)cpTokens->unspendableCCaddr,0)==0)// tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here!
returneval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
returneval->Invalid("mismatched vout0+vout2 total for selloffer");
}
elseif(ConstrainVout(tx.vout[0],1,(char*)cpTokens->unspendableCCaddr,inputs)==0)// no cc change, just vout[0]
// no cc change:
elseif(ConstrainVout(tx.vout[0],1,(char*)cpTokens->unspendableCCaddr,inputs)==0)// tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here!
returneval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
break;
case'x':// cancel
case'x':// cancel sell
//vin.0: normal input
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
throwruntime_error("tokencreate name supply description\n");
UniValueresult(UniValue::VOBJ);
std::stringname,description,hextx;
std::vector<uint8_t>nonfungibleData;
int64_tsupply;// changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected
CCerror.clear();
if(fHelp||params.size()>4||params.size()<2)
throwruntime_error("tokencreate name supply [description][data]\n");
if(ensure_CCrequirements()<0)
throwruntime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
constCKeyStore&keystore=*pwalletMain;
LOCK2(cs_main,pwalletMain->cs_wallet);
name=params[0].get_str();
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)
{
if(name.size()==0||name.size()>32){
ERR_RESULT("Token name must not be empty and up to 32 characters");
return(result);
}
if(supply<=0)
{
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(supply<=0){
ERR_RESULT("Token supply must be positive");
return(result);
}
if(params.size()==3)
{
if(params.size()>=3){
description=params[2].get_str();
if(description.size()>4096)
{
if(description.size()>4096){
ERR_RESULT("Token description must be <= 4096 characters");