Merge pull request #1233 from dimxy/non-fungible-tokens-sq

Non fungible tokens support
This commit is contained in:
jl777
2019-02-10 02:20:55 -11:00
committed by GitHub
13 changed files with 851 additions and 529 deletions

View File

@@ -29,11 +29,8 @@
bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
// CCassetsCore // CCassetsCore
//CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk); CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description); uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey, std::vector<uint8_t> &vopretNonfungible);
//CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx); bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx);
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid); int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &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 ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);

View File

@@ -43,17 +43,17 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64
int64_t unitprice,recvunitprice,newunitprice=0; int64_t unitprice,recvunitprice,newunitprice=0;
if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits);
return(false); return(false);
} }
else if ( totalunits != (remaining_units + paidunits) ) else if ( totalunits != (remaining_units + paidunits) )
{ {
fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits);
return(false); return(false);
} }
else if ( orig_nValue != (remaining_nValue + received_nValue) ) else if ( orig_nValue != (remaining_nValue + received_nValue) )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue);
return(false); return(false);
} }
else else
@@ -68,10 +68,10 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64
newunitprice = (remaining_nValue / remaining_units); newunitprice = (remaining_nValue / remaining_units);
if ( recvunitprice < unitprice ) if ( recvunitprice < unitprice )
{ {
fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
return(false); return(false);
} }
fprintf(stderr,"orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
} }
return(true); return(true);
} }
@@ -89,7 +89,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t
paidunits = totalunits; paidunits = totalunits;
received_nValue = orig_nValue; received_nValue = orig_nValue;
remaining_units = 0; remaining_units = 0;
fprintf(stderr,"totally filled!\n"); fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n");
return(true); return(true);
} }
remaining_units = (totalunits - paidunits); remaining_units = (totalunits - paidunits);
@@ -100,7 +100,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t
if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue )
{ {
remaining_nValue = (orig_nValue - received_nValue); remaining_nValue = (orig_nValue - received_nValue);
printf("total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue);
return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits));
} else return(false); } else return(false);
} }
@@ -118,14 +118,14 @@ bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,in
paid_nValue = total_nValue; paid_nValue = total_nValue;
received_assetoshis = orig_assetoshis; received_assetoshis = orig_assetoshis;
remaining_nValue = 0; remaining_nValue = 0;
fprintf(stderr,"totally filled!\n"); fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n");
return(true); return(true);
} }
remaining_nValue = (total_nValue - paid_nValue); remaining_nValue = (total_nValue - paid_nValue);
dunitprice = ((double)total_nValue / orig_assetoshis); dunitprice = ((double)total_nValue / orig_assetoshis);
received_assetoshis = (paid_nValue / dunitprice); received_assetoshis = (paid_nValue / dunitprice);
fprintf(stderr,"remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN);
fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
{ {
remaining_assetoshis = (orig_assetoshis - received_assetoshis); remaining_assetoshis = (orig_assetoshis - received_assetoshis);
@@ -138,17 +138,17 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,
int64_t unitprice,recvunitprice,newunitprice=0; int64_t unitprice,recvunitprice,newunitprice=0;
if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 ) if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue);
return(false); return(false);
} }
else if ( total_nValue != (remaining_nValue + paid_nValue) ) else if ( total_nValue != (remaining_nValue + paid_nValue) )
{ {
fprintf(stderr,"ValidateAssetRemainder: total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue);
return(false); return(false);
} }
else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) ) else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis);
return(false); return(false);
} }
else else
@@ -159,10 +159,10 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,
newunitprice = (remaining_nValue / remaining_assetoshis); newunitprice = (remaining_nValue / remaining_assetoshis);
if ( recvunitprice < unitprice ) if ( recvunitprice < unitprice )
{ {
fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
return(false); return(false);
} }
fprintf(stderr,"got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
} }
return(true); return(true);
} }
@@ -172,7 +172,7 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh
int64_t remaining_assetoshis; double dunitprice; int64_t remaining_assetoshis; double dunitprice;
if ( total_assetoshis2 == 0 ) if ( total_assetoshis2 == 0 )
{ {
fprintf(stderr,"total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2);
received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0; received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0;
return(false); return(false);
} }
@@ -181,14 +181,14 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh
paid_assetoshis2 = total_assetoshis2; paid_assetoshis2 = total_assetoshis2;
received_assetoshis = orig_assetoshis; received_assetoshis = orig_assetoshis;
remaining_assetoshis2 = 0; remaining_assetoshis2 = 0;
fprintf(stderr,"totally filled!\n"); fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n");
return(true); return(true);
} }
remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2);
dunitprice = ((double)total_assetoshis2 / orig_assetoshis); dunitprice = ((double)total_assetoshis2 / orig_assetoshis);
received_assetoshis = (paid_assetoshis2 / dunitprice); received_assetoshis = (paid_assetoshis2 / dunitprice);
fprintf(stderr,"remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN);
fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
{ {
remaining_assetoshis = (orig_assetoshis - received_assetoshis); remaining_assetoshis = (orig_assetoshis - received_assetoshis);
@@ -201,17 +201,17 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6
int64_t unitprice,recvunitprice,newunitprice=0; int64_t unitprice,recvunitprice,newunitprice=0;
if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits);
return(false); return(false);
} }
else if ( totalunits != (remaining_price + paidunits) ) else if ( totalunits != (remaining_price + paidunits) )
{ {
fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits);
return(false); return(false);
} }
else if ( orig_nValue != (remaining_nValue + received_nValue) ) else if ( orig_nValue != (remaining_nValue + received_nValue) )
{ {
fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue);
return(false); return(false);
} }
else else
@@ -222,10 +222,10 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6
newunitprice = (remaining_nValue * COIN) / remaining_price; newunitprice = (remaining_nValue * COIN) / remaining_price;
if ( recvunitprice < unitprice ) if ( recvunitprice < unitprice )
{ {
fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
return(false); return(false);
} }
fprintf(stderr,"recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
} }
return(true); return(true);
} }
@@ -279,19 +279,35 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &o
return(0); return(0);
} */ } */
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey) uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey, std::vector<uint8_t> &vopretNonfungible)
{ {
std::vector<uint8_t> vopretExtra, vopretStripped; std::vector<uint8_t> vopret1, vopret2;
uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyAssetFuncId; std::vector<uint8_t> vopretAssets; //, vopretAssetsStripped;
uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId;
uint256 dummyTokenid; uint256 dummyTokenid;
std::vector<CPubKey> voutPubkeysDummy; std::vector<CPubKey> voutPubkeysDummy;
tokenid = zeroid; tokenid = zeroid;
assetid2 = zeroid; assetid2 = zeroid;
price = 0; price = 0;
assetsEvalCode = 0;
assetsFuncId = 0;
// First - decode token opret: // First - decode token opret:
funcId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra); funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopret1, vopret2);
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl);
if (!vopret2.empty()) {
// if there are both oprets then order is like this:
vopretNonfungible = vopret1;
vopretAssets = vopret2;
}
else {
// if only one opret - it is assets opret:
vopretNonfungible.clear();
vopretAssets = vopret1;
}
/*GetOpReturnData(scriptPubKey, vopret); /*GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data(); script = (uint8_t *)vopret.data();
@@ -302,54 +318,58 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOp
//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 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()); //bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof());
if (funcId == 0 || vopretExtra.size() < 2) { if (funcId == 0 || vopretAssets.size() < 2) {
std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretExtra.size()=" << vopretExtra.size() << std::endl; LOGSTREAM("ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl);
return (uint8_t)0; return (uint8_t)0;
} }
if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size //if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size
std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretStripped" << std::endl; // std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl;
return (uint8_t)0; // return (uint8_t)0;
} //}
////tokenid = revuint256(tokenid); already done in DecodeToken! // additional check to prevent crash
evalCodeInOpret = vopretStripped.begin()[0]; if (vopretAssets.size() >= 2) {
assetFuncId = vopretStripped.begin()[1];
//std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; assetsEvalCode = vopretAssets.begin()[0];
assetsFuncId = vopretAssets.begin()[1];
if(evalCodeInOpret == EVAL_ASSETS) LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl);
{
//fprintf(stderr,"decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); if (assetsEvalCode == EVAL_ASSETS)
switch( assetFuncId )
{ {
case 'x': case 'o': //fprintf(stderr,"DecodeAssetTokenOpRet() decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId);
if (vopretStripped.size() == 2) // no data after 'evalcode assetFuncId' allowed switch (assetsFuncId)
{
case 'x': case 'o':
if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed
{ {
return(assetFuncId); return(assetsFuncId);
} }
break; break;
case 's': case 'b': case 'S': case 'B': case 's': case 'b': case 'S': case 'B':
if (E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0)
{ {
//fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
return(assetFuncId); return(assetsFuncId);
} }
break; break;
case 'E': case 'e': case 'E': case 'e':
if ( E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0)
{ {
//fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
assetid2 = revuint256(assetid2); assetid2 = revuint256(assetid2);
return(assetFuncId); return(assetsFuncId);
} }
break; break;
default: default:
fprintf(stderr,"DecodeAssetTokenOpRet: illegal assetFuncId.%02x\n", assetFuncId); LOGSTREAM("ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() illegal assetFuncId=" << (int)funcId << std::endl);
//funcId = 0;
break; break;
}
} }
} }
LOGSTREAM("ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl);
return (uint8_t)0; return (uint8_t)0;
} }
@@ -358,49 +378,55 @@ bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CT
{ {
uint256 assetid,assetid2; uint256 assetid,assetid2;
uint8_t evalCode; uint8_t evalCode;
if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) std::vector<uint8_t> vopretNonfungibleDummy;
if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy) != 0 )
return(true); return(true);
else else
return(false); return(false);
} }
// Calculate sell/buy owner's source token/asset address from ask/bid tx // Calculate seller/buyer's dest cc address from ask/bid tx funcid
bool GetAssetorigaddrs(struct CCcontract_info *cp, char *userCCaddr, char *destaddr, const CTransaction& tx) bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx)
{ {
uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; uint256 assetid, assetid2;
int64_t price,nValue=0;
int32_t n;
uint8_t vintxFuncId;
std::vector<uint8_t> origpubkey; std::vector<uint8_t> origpubkey;
CScript script; CScript script;
uint8_t evalCode; uint8_t evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
n = tx.vout.size(); n = vintx.vout.size();
if( n == 0 || (funcid = DecodeAssetTokenOpRet(tx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy)) == 0 )
return(false); return(false);
bool bGetCCaddr = false; bool bGetCCaddr = false;
if (funcid == 's' || funcid == 'S') { struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpTokens, tokensC; cpTokens = CCinit(&tokensC, EVAL_TOKENS);
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey)); if (vintxFuncId == 's' || vintxFuncId == 'S') {
//bGetCCaddr = GetTokensCCaddress(cp, CCaddr, pubkey2pk(origpubkey)); // bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey));
cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
} }
else if (funcid == 'b' || funcid == 'B') { else if (vintxFuncId == 'b' || vintxFuncId == 'B') {
struct CCcontract_info *cpTokens, tokensC; cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
cpTokens = CCinit(&tokensC, EVAL_TOKENS); bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey));
} }
else { else {
std::cerr << "GetAssetorigaddrs incorrect funcid=" << (char)(funcid?funcid:' ') << std::endl; std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl;
return false; return false;
} }
if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG))
if( bGetCCaddr && Getscriptaddress(destaddr, CScript() << origpubkey << OP_CHECKSIG))
return(true); return(true);
else else
return(false); return(false);
} }
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{ {
uint256 hashBlock; uint256 hashBlock;
uint256 assetid, assetid2; uint256 assetid, assetid2;
@@ -410,7 +436,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch
char destaddr[64], unspendableAddr[64]; char destaddr[64], unspendableAddr[64];
origaddr[0] = destaddr[0] = CCaddr[0] = 0; origaddr[0] = destaddr[0] = origCCaddr[0] = 0;
uint8_t funcid = 0; uint8_t funcid = 0;
if (tx.vout.size() > 0) { if (tx.vout.size() > 0) {
@@ -418,7 +444,9 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch
int64_t tmpprice; int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey; std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode; uint8_t evalCode;
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); std::vector<uint8_t> vopretNonfungibleDummy;
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy);
} }
if( tx.vin.size() < 2 ) if( tx.vin.size() < 2 )
@@ -427,45 +455,48 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch
return eval->Invalid("vin1 needs to be buyvin.vout[0]"); return eval->Invalid("vin1 needs to be buyvin.vout[0]");
else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 ) else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 )
{ {
/* int32_t z; std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&tx.vin[vini].prevout.hash)[z]);
fprintf(stderr," vini.%d\n",vini); */
std::cerr << "AssetValidateCCvin cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl;
return eval->Invalid("always should find CCvin, but didnt"); return eval->Invalid("always should find CCvin, but didnt");
} }
// if fillSell or cancelSell --> to spend tokens from dual-eval token-assets unspendable addr // check source cc unspendable cc address:
// if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr
else if( (funcid == 'S' || funcid == 'x') && else if( (funcid == 'S' || funcid == 'x') &&
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
!GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || !GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
strcmp(destaddr, unspendableAddr) != 0)) strcmp(destaddr, unspendableAddr) != 0))
{ {
fprintf(stderr,"AssetValidateCCvin cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
return eval->Invalid("invalid vin AssetsCCaddr"); return eval->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
else if ((funcid == 'B' || funcid == 'o') && else if ((funcid == 'B' || funcid == 'o') &&
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
!GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || !GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
strcmp(destaddr, unspendableAddr) != 0)) strcmp(destaddr, unspendableAddr) != 0))
{ {
fprintf(stderr, "AssetValidateCCvin cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
return eval->Invalid("invalid vin AssetsCCaddr"); return eval->Invalid("invalid vin assets CCaddr");
} }
// end of check source unspendable cc address
//else if ( vinTx.vout[0].nValue < 10000 ) //else if ( vinTx.vout[0].nValue < 10000 )
// return eval->Invalid("invalid dust for buyvin"); // return eval->Invalid("invalid dust for buyvin");
else if( GetAssetorigaddrs(cp, CCaddr, origaddr, vinTx) == 0 ) // get user dest cc and normal addresses:
else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 )
return eval->Invalid("couldnt get origaddr for buyvin"); return eval->Invalid("couldnt get origaddr for buyvin");
fprintf(stderr,"AssetValidateCCvin got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
if ( vinTx.vout[0].nValue == 0 ) if ( vinTx.vout[0].nValue == 0 )
return eval->Invalid("null value CCvin"); return eval->Invalid("null value CCvin");
return(vinTx.vout[0].nValue); return(vinTx.vout[0].nValue);
} }
int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid) int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid)
{ {
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
CCaddr[0] = origaddr[0] = 0; CCaddr[0] = origaddr[0] = 0;
// validate locked coins on Assets vin[1] // validate locked coins on Assets vin[1]
@@ -473,7 +504,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
return(0); return(0);
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout0 for buyvin"); return eval->Invalid("invalid normal vout0 for buyvin");
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy)) == 'b' &&
vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'?
return eval->Invalid("invalid normal vout1 for buyvin"); return eval->Invalid("invalid normal vout1 for buyvin");
else else
@@ -493,7 +524,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid) int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid)
{ {
CTransaction vinTx; int64_t nValue,assetoshis; CTransaction vinTx; int64_t nValue,assetoshis;
//fprintf(stderr,"AssetValidateSellvin\n"); //fprintf(stderr,"AssetValidateSellvin()\n");
if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
return(0); return(0);
if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 ) if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 )
@@ -508,13 +539,14 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr
uint256 assetidOpret, assetidOpret2; uint256 assetidOpret, assetidOpret2;
uint8_t funcid, evalCode; uint8_t funcid, evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
// this is just for log messages indentation fur debugging recursive calls: // this is just for log messages indentation fur debugging recursive calls:
int32_t n = tx.vout.size(); int32_t n = tx.vout.size();
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0) if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey, vopretNonfungibleDummy)) == 0)
{ {
std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned null for the opret for txid=" << tx.GetHash().GetHex() << std::endl; std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl;
return(false); return(false);
} }
/* it is now on token level: /* it is now on token level:
@@ -556,22 +588,20 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr
} }
// Checks if the vout is a really Asset CC vout // Checks if the vout is a really Asset 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 IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid) int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid)
{ {
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl; //std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
int32_t n = 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 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_t n = 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): // moved opret checking to this new reusable func (dimxy):
const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey); const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey);
//std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; //std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
@@ -627,12 +657,9 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t
} }
} }
for (int32_t i = 0; i < numvouts; i++) for (int32_t i = 0; i < numvouts-1; i++)
{ {
// Note: we pass in here 'false' because we don't need to call AssetExactAmounts() recursively from IsAssetvout
// indeed, in this case we'll be checking this tx again
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid); assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid);
if (assetoshis != 0) if (assetoshis != 0)
{ {
std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl;
@@ -643,12 +670,6 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t
//std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; //std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
/* we do not verify inputs == outputs here, /* we do not verify inputs == outputs here,
it's done in Tokens: it's now done in Tokens */
if (inputs != outputs) {
if (tx.GetHash() != assetid) {
std::cerr << "AssetCalcAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs");
}
} */
return(true); return(true);
} }

View File

@@ -14,9 +14,9 @@
******************************************************************************/ ******************************************************************************/
#include "CCassets.h" #include "CCassets.h"
//#include "CCtokens.h" #include "CCtokens.h"
/* use AddTokenCCInputs instead
int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs) int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs)
{ {
char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t j,vout,n = 0; char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t j,vout,n = 0;
@@ -64,25 +64,19 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK
//std::cerr << "AddAssetInputs() found totalinputs=" << totalinputs << std::endl; //std::cerr << "AddAssetInputs() found totalinputs=" << totalinputs << std::endl;
return(totalinputs); return(totalinputs);
} }
*/
UniValue AssetOrders(uint256 refassetid) UniValue AssetOrders(uint256 refassetid)
{ {
static uint256 zero; static uint256 zero;
UniValue result(UniValue::VARR); UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsAssets;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it) auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it)
{ {
uint256 txid, hashBlock, assetid, assetid2; uint256 txid, hashBlock, assetid, assetid2;
int64_t price; int64_t price;
std::vector<uint8_t> origpubkey; std::vector<uint8_t> origpubkey;
std::vector<uint8_t> vopretNonfungible;
CTransaction vintx; CTransaction vintx;
uint8_t funcid, evalCode; uint8_t funcid, evalCode;
char numstr[32], funcidstr[16], origaddr[64], assetidstr[65]; char numstr[32], funcidstr[16], origaddr[64], assetidstr[65];
@@ -93,7 +87,7 @@ UniValue AssetOrders(uint256 refassetid)
{ {
// for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); // for logging: 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 : ' ') << " assetid=" << assetid.GetHex() << 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 = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungible)) != 0)
{ {
if (refassetid != zero && assetid != refassetid) if (refassetid != zero && assetid != refassetid)
{ {
@@ -163,25 +157,35 @@ UniValue AssetOrders(uint256 refassetid)
} }
}; };
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsCoins;
struct CCcontract_info *cpAssets, assetsC;
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
char assetsUnspendableAddr[64]; char assetsUnspendableAddr[64];
GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL)); GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsAssets, assetsUnspendableAddr /*(char *)cpTokens->unspendableCCaddr*/); SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr);
char tokensUnspendableAddr[64]; char assetsTokensUnspendableAddr[64];
GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); std::vector<uint8_t> vopretNonfungible;
SetCCunspents(unspentOutputsAssets, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/); GetNonfungibleData(refassetid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin(); // tokenasks:
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itCoins = unspentOutputsCoins.begin();
itCoins != unspentOutputsCoins.end();
itCoins++)
addOrders(cpAssets, itCoins);
// tokenbids:
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin();
itTokens != unspentOutputsTokens.end(); itTokens != unspentOutputsTokens.end();
itTokens++) itTokens++)
addOrders(cpTokens, itTokens); addOrders(cpAssets, itTokens);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itAssets = unspentOutputsAssets.begin();
itAssets != unspentOutputsAssets.end();
itAssets++)
addOrders(cpAssets, itAssets);
return(result); return(result);
} }
@@ -328,14 +332,14 @@ std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, in
return (""); return ("");
} }
CPubKey unspendablePubkey = GetUnspendable(cpAssets, 0); CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendablePubkey)); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk));
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, 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())))); 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"); CCerror = strprintf("no coins found to make buy offer");
return(""); return("");
@@ -348,7 +352,6 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
CPubKey mypk; CPubKey mypk;
uint64_t mask; uint64_t mask;
int64_t inputs, CCchange; int64_t inputs, CCchange;
CScript opret;
struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpAssets, assetsC;
struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpTokens, tokensC;
@@ -359,7 +362,8 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
return(""); return("");
} }
cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: this is for signing cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing
if (txfee == 0) if (txfee == 0)
txfee = 10000; txfee = 10000;
@@ -367,10 +371,11 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
mypk = pubkey2pk(Mypubkey()); mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
{ {
std::vector<uint8_t> vopretNonfungible;
mask = ~((1LL << mtx.vin.size()) - 1); mask = ~((1LL << mtx.vin.size()) - 1);
// add single-eval tokens: // add single-eval tokens (or non-fungible tokens):
cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: tokens is here cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60)) > 0) if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0)
{ {
if (inputs < askamount) { if (inputs < askamount) {
//was: askamount = inputs; //was: askamount = inputs;
@@ -379,20 +384,26 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
return (""); return ("");
} }
CPubKey unspendablePubkey = GetUnspendable(cpAssets, NULL); // if this is non-fungible tokens:
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, askamount, unspendablePubkey)); if( !vopretNonfungible.empty() )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker // set its evalcode
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL);
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->additionalTokensEvalcode2, askamount, unspendableAssetsPubkey));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders)
if (inputs > askamount) if (inputs > askamount)
CCchange = (inputs - askamount); CCchange = (inputs - askamount);
if (CCchange != 0) if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // change to single-eval token vout // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible)
mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk));
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(unspendablePubkey); voutTokenPubkeys.push_back(unspendableAssetsPubkey);
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())); EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
return(FinalizeCCTx(mask,cpAssets, mtx, mypk, txfee, opret)); EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())));
} }
else { else {
fprintf(stderr, "need some tokens to place ask\n"); fprintf(stderr, "need some tokens to place ask\n");
@@ -430,7 +441,7 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{ {
mask = ~((1LL << mtx.vin.size()) - 1); mask = ~((1LL << mtx.vin.size()) - 1);
if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0)
{ {
////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// ////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
if (inputs < askamount) { if (inputs < askamount) {
@@ -458,13 +469,13 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a
else { else {
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey())); EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey()));
} }
////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// ////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
} }
else { else {
fprintf(stderr, "need some assets to place ask\n"); fprintf(stderr, "need some assets to place ask\n");
} } */
} }
else { // dimxy added 'else', because it was misleading message before else { // dimxy added 'else', because it was misleading message before
fprintf(stderr,"need some native coins to place ask\n"); fprintf(stderr,"need some native coins to place ask\n");
@@ -494,12 +505,13 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
mask = ~((1LL << mtx.vin.size()) - 1); mask = ~((1LL << mtx.vin.size()) - 1);
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{ {
std::vector<uint8_t> vopretNonfungible;
bidamount = vintx.vout[0].nValue; bidamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets
if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0)
{ {
if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b'
else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B'
} }
@@ -510,7 +522,7 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))); EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))));
} }
} }
@@ -523,8 +535,12 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint64_t mask; CTransaction vintx; uint64_t mask;
uint256 hashBlock; int64_t askamount; uint256 hashBlock; int64_t askamount;
CPubKey mypk; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; CPubKey mypk;
uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector<uint8_t> dummyOrigpubkey; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC;
uint8_t funcid, dummyEvalCode;
uint256 dummyAssetid, dummyAssetid2;
int64_t dummyPrice;
std::vector<uint8_t> dummyOrigpubkey;
cpAssets = CCinit(&assetsC, EVAL_ASSETS); cpAssets = CCinit(&assetsC, EVAL_ASSETS);
@@ -538,27 +554,27 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
mask = ~((1LL << mtx.vin.size()) - 1); mask = ~((1LL << mtx.vin.size()) - 1);
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{ {
std::vector<uint8_t> vopretNonfungible;
askamount = vintx.vout[0].nValue; askamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0)
{ {
if (funcid == 's') mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' if (funcid == 's')
else if (funcid=='S') mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s'
else if (funcid=='S')
mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S'
} }
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk)); // one-eval token vout if (vopretNonfungible.size() > 0)
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
mtx.vout.push_back(MakeTokensCC1vout(cpAssets->additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : cpAssets->additionalTokensEvalcode2, askamount, mypk)); // one-eval token vout
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk); voutTokenPubkeys.push_back(mypk);
/* char myCCaddr[65];
uint8_t myPrivkey[32];
Myprivkey(myPrivkey);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
GetCCaddress(cpAssets, myCCaddr, mypk); */
// this is only for unspendable addresses: // this is only for unspendable addresses:
//CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress //CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress
@@ -572,7 +588,7 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))); EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))));
} }
} }
@@ -614,8 +630,9 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f
SetAssetOrigpubkey(origpubkey, origprice, vintx); SetAssetOrigpubkey(origpubkey, origprice, vintx);
mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60)) > 0) std::vector<uint8_t> vopretNonfungible;
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0)
{ {
if (inputs < fillamount) { if (inputs < fillamount) {
std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl; std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl;
@@ -624,6 +641,10 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f
} }
SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice); SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice);
uint8_t additionalTokensEvalcode2 = 0;
if (vopretNonfungible.size() > 0)
additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
if (inputs > fillamount) if (inputs > fillamount)
CCchange = (inputs - fillamount); CCchange = (inputs - fillamount);
@@ -634,13 +655,13 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal 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(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 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey
if (CCchange != 0) if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens
fprintf(stderr,"FillBuyOffer remaining %llu -> origpubkey\n", (long long)remaining_required); fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required);
char unspendableAssetsAddr[64]; char unspendableAssetsAddr[64];
cpAssets = CCinit(&assetsC, EVAL_ASSETS); cpAssets = CCinit(&assetsC, EVAL_ASSETS);
@@ -653,8 +674,8 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); voutTokenPubkeys.push_back(pubkey2pk(origpubkey));
return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee, return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))); EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))));
} else return("dont have any assets to fill bid"); } else return("dont have any assets to fill bid");
} }
@@ -691,15 +712,21 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
return(""); return("");
} }
std::vector<uint8_t> vopretNonfungible;
uint8_t additionalTokensEvalcode2 = 0;
GetNonfungibleData(assetid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
cpAssets = CCinit(&assetsC, EVAL_ASSETS); cpAssets = CCinit(&assetsC, EVAL_ASSETS);
if (txfee == 0) if (txfee == 0)
txfee = 10000; txfee = 10000;
mypk = pubkey2pk(Mypubkey()); mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) //if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
{ //{
mask = ~((1LL << mtx.vin.size()) - 1); //mask = ~((1LL << mtx.vin.size()) - 1);
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{ {
orig_assetoshis = vintx.vout[askvout].nValue; orig_assetoshis = vintx.vout[askvout].nValue;
@@ -707,13 +734,12 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
dprice = (double)total_nValue / orig_assetoshis; dprice = (double)total_nValue / orig_assetoshis;
paid_nValue = dprice * fillunits; paid_nValue = dprice * fillunits;
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 = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet
if (assetid2 != zeroid) }
inputs = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet
else else
{ {
inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60); 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
mask = ~((1LL << mtx.vin.size()) - 1); mask = ~((1LL << mtx.vin.size()) - 1);
} }
if (inputs > 0) if (inputs > 0)
@@ -723,7 +749,10 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
CCerror = strprintf("insufficient coins to fill sell"); CCerror = strprintf("insufficient coins to fill sell");
return (""); return ("");
} }
// cc vin should be after normal vin
mtx.vin.push_back(CTxIn(asktxid, askvout, CScript()));
if (assetid2 != zeroid) if (assetid2 != zeroid)
SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet
else else
@@ -732,11 +761,11 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
if (assetid2 != zeroid && inputs > paid_nValue) if (assetid2 != zeroid && inputs > paid_nValue)
CCchange = (inputs - paid_nValue); CCchange = (inputs - paid_nValue);
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // vout.0 tokens remainder to unspendable cc addr // 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 mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL)));
//vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr:
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk));
// NOTE: no marker here
if (assetid2 != zeroid) { if (assetid2 != zeroid) {
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl;
// TODO: change MakeCC1vout appropriately when implementing: // TODO: change MakeCC1vout appropriately when implementing:
@@ -768,14 +797,16 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk); voutTokenPubkeys.push_back(mypk);
cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2;
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))); EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))));
} else { } else {
CCerror = strprintf("filltx not enough utxos"); CCerror = strprintf("filltx not enough utxos");
fprintf(stderr,"%s\n", CCerror.c_str()); fprintf(stderr,"%s\n", CCerror.c_str());
} }
} }
} //}
return(""); return("");
} }

View File

@@ -290,6 +290,9 @@ int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode)
struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
{ {
// important to clear because not all members are always initialized!
memset(cp, '\0', sizeof(*cp));
cp->evalcode = evalcode; cp->evalcode = evalcode;
switch ( evalcode ) switch ( evalcode )
{ {

View File

@@ -89,6 +89,7 @@ struct CCcontract_info
{ {
// this is for spending from 'unspendable' CC address // this is for spending from 'unspendable' CC address
uint8_t evalcode; uint8_t evalcode;
uint8_t additionalTokensEvalcode2; // this is for making three-eval-token vouts (EVAL_TOKENS + evalcode + additionalEvalcode2)
char unspendableCCaddr[64], CChexstr[72], normaladdr[64]; char unspendableCCaddr[64], CChexstr[72], normaladdr[64];
uint8_t CCpriv[32]; uint8_t CCpriv[32];
@@ -103,7 +104,7 @@ struct CCcontract_info
// this is for spending from two additional 'unspendable' CC addresses of other eval codes // this is for spending from two additional 'unspendable' CC addresses of other eval codes
// (that is, for spending from several cc contract 'unspendable' addresses): // (that is, for spending from several cc contract 'unspendable' addresses):
uint8_t evalcode2, evalcode3; uint8_t unspendableEvalcode2, unspendableEvalcode3; // changed evalcodeN to unspendableEvalcodeN for not mixing up with additionalEvalcodeN
char unspendableaddr2[64], unspendableaddr3[64]; char unspendableaddr2[64], unspendableaddr3[64];
uint8_t unspendablepriv2[32], unspendablepriv3[32]; uint8_t unspendablepriv2[32], unspendablepriv3[32];
CPubKey unspendablepk2, unspendablepk3; CPubKey unspendablepk2, unspendablepk3;
@@ -160,17 +161,19 @@ 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 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 AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, 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, std::vector<uint8_t> &vopretNonfungible);
int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid);
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, std::vector<uint8_t> vopretNonfungible);
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload); //old version
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload); CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload);
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload); CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<uint8_t> vpayloadNonfungible, CScript payload);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description); uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, std::vector<uint8_t> &vopretNonfungible);
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra); uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra);
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopret1, std::vector<uint8_t> &vopret2);
uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector <uint8_t>&data); uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector <uint8_t>&data);
int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen); int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen);
@@ -193,12 +196,14 @@ CC* GetCryptoCondition(CScript const& scriptSig);
void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr);
void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr);
void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr);
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2); CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2);
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2);
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk);
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk);
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2);
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2);
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk);
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk);
bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk);
bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2);
void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr);
@@ -245,4 +250,29 @@ void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uin
bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen); bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen);
UniValue ValueFromAmount(const CAmount& amount); UniValue ValueFromAmount(const CAmount& amount);
// bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream:
#define CCLOG_INFO 0
#define CCLOG_DEBUG1 1
#define CCLOG_DEBUG2 2
#define CCLOG_DEBUG3 3
template <class T>
inline void CCLogPrintStream(char *category, int level, T print_to_stream)
{
std::ostringstream stream;
print_to_stream(stream);
if (level < 0)
level = 0;
if (level > 3)
level = 3;
for (int i = 0; i < level; i++)
if (LogAcceptCategory((std::string(category) + (level > 0 ? std::string("-") + std::to_string(level) : std::string(""))).c_str())) {
LogPrintStr(stream.str());
break;
}
}
#define LOGSTREAM(category, level, logoperator) CCLogPrintStream( category, level, [=](std::ostringstream &stream) {logoperator;} )
#endif #endif

View File

@@ -43,15 +43,25 @@
// NOTE: this inital tx won't be used by other contract // NOTE: this inital tx won't be used by other contract
// for tokens to be used there should be at least one 't' tx with other contract's custom opret // for tokens to be used there should be at least one 't' tx with other contract's custom opret
CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description) CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, std::vector<uint8_t> vopretNonfungible )
{ {
CScript opret; uint8_t evalcode = EVAL_TOKENS; CScript opret;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); uint8_t evalcode = EVAL_TOKENS;
funcid = 'c'; // override the param
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \
if(!vopretNonfungible.empty()) ss << vopretNonfungible );
return(opret); return(opret);
} }
// this is for other contracts which use tokens and build customized extra payloads to token's opret: // this is for other contracts which use tokens and build customized extra payloads to token's opret:
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload) CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload)
{
std::vector<uint8_t> vpayloadNonfungibleEmpty;
return EncodeTokenOpRet(tokenid, voutPubkeys, vpayloadNonfungibleEmpty, payload);
}
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<uint8_t> vpayloadNonfungible, CScript payload)
{ {
CScript opret; CScript opret;
uint8_t tokenFuncId = 't'; uint8_t tokenFuncId = 't';
@@ -60,17 +70,20 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScr
tokenid = revuint256(tokenid); tokenid = revuint256(tokenid);
uint8_t ccType = 0; uint8_t ccType = 0;
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2)
ccType = voutPubkeys.size(); ccType = voutPubkeys.size();
else {
LOGSTREAM("cctokens", CCLOG_INFO, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl);
}
std::vector<uint8_t> vpayload; std::vector<uint8_t> vpayload;
GetOpReturnData(payload, vpayload); GetOpReturnData(payload, vpayload);
opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \ opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \
if (ccType >= 1) ss << voutPubkeys[0]; \ if (ccType >= 1) ss << voutPubkeys[0]; \
if (ccType == 2) ss << voutPubkeys[1]; \ if (ccType == 2) ss << voutPubkeys[1]; \
if (vpayload.size() > 0) ss << vpayload;); if (vpayloadNonfungible.size() > 0) ss << vpayloadNonfungible; \
if (vpayload.size() > 0) ss << vpayload);
// "error 64: scriptpubkey": // "error 64: scriptpubkey":
// if (payload.size() > 0) // if (payload.size() > 0)
@@ -96,21 +109,50 @@ CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 t
return EncodeTokenOpRet(tokenid, voutPubkeys, payload); return EncodeTokenOpRet(tokenid, voutPubkeys, payload);
} }
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description) // overload for fungible:
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description) {
std::vector<uint8_t> vopretNonfungibleDummy;
return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, vopretNonfungibleDummy);
}
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description, std::vector<uint8_t> &vopretNonfungible)
{ {
std::vector<uint8_t> vopret; uint8_t dummyEvalcode, funcid, *script; std::vector<uint8_t> vopret; uint8_t dummyEvalcode, funcid, *script;
GetOpReturnData(scriptPubKey, vopret); GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data(); script = (uint8_t *)vopret.data();
if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' ) if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' )
{ {
if ( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 ) if( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; \
// we suppose in 'c' opret it might be only non-fungible payload and not any assets/heir/etc payloads
if(!ss.eof()) ss >> vopretNonfungible ) )
return(funcid); return(funcid);
} }
return (uint8_t)0; return (uint8_t)0;
} }
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) // overload for compatibility allows only usual fungible tokens:
// warning: it makes vopret marshalling to CScript because this is what caller would expect
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) {
std::vector<uint8_t> vopret1, vopret2;
uint8_t funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutPubkeys, vopret1, vopret2);
CScript opretExtra;
vopretExtra.clear();
// make marshalling for compatibility
// callers of this func expect length of full array at the beginning (and they will make 'vopretStripped' from vopretExtra)
if (vopret2.empty())
opretExtra << OP_RETURN << E_MARSHAL(ss << vopret1); // if first opret (or no oprets)
else
opretExtra << OP_RETURN << E_MARSHAL(ss << vopret2); // if both oprets present, return assets/heir/gateways/... opret (dump non-fungible opret)
GetOpReturnData(opretExtra, vopretExtra);
return funcId;
}
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopret1, std::vector<uint8_t> &vopret2)
{ {
std::vector<uint8_t> vopret, extra, dummyPubkey; std::vector<uint8_t> vopret, extra, dummyPubkey;
uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType;
@@ -121,32 +163,39 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256
script = (uint8_t *)vopret.data(); script = (uint8_t *)vopret.data();
tokenid = zeroid; tokenid = zeroid;
if (script != 0 && vopret.size() > 2) if (script != NULL && vopret.size() > 2)
{ {
// 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! // 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; bool isEof = true;
evalCode = script[0]; evalCodeTokens = script[0];
if (evalCode != EVAL_TOKENS) if (evalCodeTokens != EVAL_TOKENS)
return (uint8_t)0; return (uint8_t)0;
funcId = script[1]; funcId = script[1];
//fprintf(stderr,"decode.[%c]\n",funcId); LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' '));
switch( funcId ) switch( funcId )
{ {
case 'c': case 'c':
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription); return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vopret1);
//break; //break;
case 't': case 't':
//not used yet: case 'l': //not used yet: case 'l':
// NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error' // 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<uint8_t>(ss.begin(), ss.end())) if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; \
|| !isEof) if (ccType >= 1) ss >> voutPubkey1; \
if (ccType == 2) ss >> voutPubkey2; \
isEof = ss.eof(); \
if (!isEof) ss >> vopret1; \
isEof = ss.eof(); \
if (!isEof) { ss >> vopret2; } \
// if something else remains -> bad format
isEof = ss.eof()) || !isEof)
{ {
if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType
std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl);
return (uint8_t)0; return (uint8_t)0;
} }
@@ -160,22 +209,20 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256
tokenid = revuint256(tokenid); tokenid = revuint256(tokenid);
return(funcId); return(funcId);
} }
std::cerr << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl);
return (uint8_t)0; return (uint8_t)0;
default: default:
std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl);
return (uint8_t)0; return (uint8_t)0;
} }
} }
else { else {
std::cerr << "DecodeTokenOpRet() empty opret, could not parse" << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() empty opret, could not parse" << std::endl);
} }
return (uint8_t)0; return (uint8_t)0;
} }
// tx validation // tx validation
bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
{ {
@@ -183,7 +230,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore;
std::vector<uint8_t> vopretExtra, tmporigpubkey, ignorepubkey; std::vector<uint8_t> vopret1, vopret2, tmporigpubkey, ignorepubkey;
uint8_t funcid, evalCodeInOpret; uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], CCaddr[64]; char destaddr[64], origaddr[64], CCaddr[64];
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
@@ -195,10 +242,10 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
outputs = inputs = 0; outputs = inputs = 0;
preventCCvins = preventCCvouts = -1; preventCCvins = preventCCvouts = -1;
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0) if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopret1, vopret2)) == 0)
return eval->Invalid("TokenValidate: invalid opreturn payload"); return eval->Invalid("TokenValidate: invalid opreturn payload");
fprintf(stderr, "TokensValidate (%c) evalcode=0x%0x\n", funcid, cp->evalcode); LOGSTREAM("cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << cp->evalcode << std::endl);
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
return eval->Invalid("cant find token create txid"); return eval->Invalid("cant find token create txid");
@@ -218,6 +265,12 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
} }
} }
// non-fungible tokens validation:
std::vector<uint8_t> vopretNonfungible;
GetNonfungibleData(tokenid, vopretNonfungible);
if (vopretNonfungible.size() > 0 && vopretNonfungible != vopret1) // assuming tx vopretNonfungible in vopret1
return eval->Invalid("incorrect or empty non-fungible data");
switch (funcid) switch (funcid)
{ {
@@ -240,11 +293,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
if (inputs == 0) if (inputs == 0)
return eval->Invalid("no token inputs for transfer"); return eval->Invalid("no token inputs for transfer");
fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts); LOGSTREAM("cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl);
break; // breaking to other contract validation... break; // breaking to other contract validation...
default: default:
fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid); LOGSTREAM("cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl);
return eval->Invalid("unexpected token funcid"); return eval->Invalid("unexpected token funcid");
} }
@@ -266,7 +319,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
// helper funcs: // helper funcs:
// extract my vins pubkeys: // extract cc token vins' pubkeys:
bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys) { bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys) {
bool found = false; bool found = false;
@@ -276,12 +329,13 @@ bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys)
cpTokens = CCinit(&tokensC, EVAL_TOKENS); cpTokens = CCinit(&tokensC, EVAL_TOKENS);
for (int32_t i = 0; i < tx.vin.size(); i++) for (int32_t i = 0; i < tx.vin.size(); i++)
{ // check for additional contracts which may send tokens to the Tokens contract {
// check for cc token vins:
if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) )
{ {
auto findEval = [](CC *cond, struct CCVisitor _) { auto findEval = [](CC *cond, struct CCVisitor _) {
bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS; bool r = false;
if (cc_typeId(cond) == CC_Secp256k1) { if (cc_typeId(cond) == CC_Secp256k1) {
*(CPubKey*)_.context = buf2pk(cond->publicKey); *(CPubKey*)_.context = buf2pk(cond->publicKey);
@@ -313,38 +367,49 @@ bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys)
thread_local uint32_t tokenValIndentSize = 0; thread_local uint32_t tokenValIndentSize = 0;
// validates opret for token tx: // validates opret for token tx:
uint8_t ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) { uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) {
uint256 tokenidOpret, tokenidOpret2; uint256 tokenidOpret = zeroid;
uint8_t funcid; uint8_t funcid;
uint8_t dummyEvalCode; uint8_t dummyEvalCode;
std::vector<CPubKey> voutPubkeysDummy;
std::vector<uint8_t> vopretExtraDummy;
// this is just for log messages indentation fur debugging recursive calls: // this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.'); std::string indentStr = std::string().append(tokenValIndentSize, '.');
int32_t n = tx.vout.size(); if (tx.vout.size() == 0)
return (uint8_t)0;
if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, vopretExtraDummy)) == 0)
{ {
std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl);
return(false); return (uint8_t)0;
} }
else if (funcid == 'c') else if (funcid == 'c')
{ {
if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) { if (tokenid != zeroid && tokenid == tx.GetHash()) {
//std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
return funcid; return funcid;
} }
else {
LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl);
}
} }
else if (funcid == 't') else if (funcid == 't')
{ {
//std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (tokenid != zeroid && tokenid == tokenidOpret) { if (tokenid != zeroid && tokenid == tokenidOpret) {
//std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
return funcid; return funcid;
} }
else {
LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl);
}
} }
//std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; else {
LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl);
}
return (uint8_t)0; return (uint8_t)0;
} }
@@ -358,20 +423,20 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
// this is just for log messages indentation fur debugging recursive calls: // this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.'); 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;
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl);
int32_t n = tx.vout.size();
// just check boundaries:
if (n == 0 || v < 0 || v >= n-1) {
LOGSTREAM("cctokens", 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! //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 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:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
if (goDeeper) { if (goDeeper) {
//std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl;
//validate all tx //validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
@@ -384,98 +449,109 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
// if ccInputs != ccOutputs and it is not the tokenbase tx // if ccInputs != ccOutputs and it is not the tokenbase tx
// this means it is possibly a fake tx (dimxy): // 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) 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("cctokens", 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);
return 0; return 0;
} }
} }
} }
// moved opret checking to this new reusable func (dimxy): // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'):
std::vector<CPubKey> voutPubkeys; const uint8_t funcId = ValidateTokenOpret(tx, reftokenid);
std::vector<uint8_t> vopretExtra;
const uint8_t funcId = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra);
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (funcId != 0) { if (funcId != 0) {
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
if (checkPubkeys && funcId != 'c') { // verify that the vout is token's (for 'c' there is no pubkeys!): uint8_t dummyEvalCode;
uint256 tokenIdOpret;
std::vector<CPubKey> voutPubkeys;
std::vector<uint8_t> vopret1;
std::vector<uint8_t> vopret2;
DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopret1, vopret2);
//std::cerr << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl; if (checkPubkeys && funcId != 'c') { // for 'c' there is no pubkeys
// verify that the vout is token by constructing vouts with the pubkeys in the opret:
uint8_t evalCodeInOpret;
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("cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret1=" << HexStr(vopret1) << std::endl);
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret2=" << HexStr(vopret2) << std::endl);
uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
uint8_t evalCode2 = 0; // will be checked if zero or not
// NOTE: evalcode order in vouts is important:
// non-fungible-eval -> EVAL_TOKENS -> assets-eval
if (vopret1.size() > 0) {
evalCode = vopret1.begin()[0];
}
if (vopret2.size() > 0) {
evalCode2 = vopret2.begin()[0];
}
// checking vouts for possible token use-cases:
std::vector<std::pair<CTxOut, std::string>> testVouts;
// maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout?
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) {
CTxOut testDualVout; // check dual/three-eval 1 pubkey vout with the first pubkey
// check dual-eval 1 pubkey vout with the first pubkey testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) );
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0]); if (evalCode2 != 0)
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { // also check in backward evalcode order
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=0), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) );
return tx.vout[v].nValue;
}
if(voutPubkeys.size() == 2) {
// check dual eval 1of2 pubkeys vout
testDualVout = MakeTokensCC1of2vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]);
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token 1of2 vout, eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
// check dual eval 1 pubkey vout with the second pubkey if(voutPubkeys.size() == 2) {
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[1]); // check dual/three eval 1of2 pubkeys vout
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) );
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=1), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; // check dual/three eval 1 pubkey vout with the second pubkey
return tx.vout[v].nValue; testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]")));
} if (evalCode2 != 0) {
// also check in backward evalcode order:
// check dual/three eval 1of2 pubkeys vout
testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval")));
// check dual/three eval 1 pubkey vout with the second pubkey
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval")));
}
} }
// maybe this is claim to single-eval token? // maybe this is like gatewayclaim to single-eval token?
CTxOut testTokenVout1; testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]")));
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]); // maybe this is like FillSell for non-fungible token?
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { if( evalCode != 0 )
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=0), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]")));
return tx.vout[v].nValue; if( evalCode2 != 0 )
} testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]")));
if (voutPubkeys.size() == 2) { if (voutPubkeys.size() == 2) {
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]); // the same for pk[1]:
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]")));
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=1), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; if (evalCode != 0)
return tx.vout[v].nValue; testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]")));
} if (evalCode2 != 0)
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]")));
} }
} }
// maybe it is single-eval or dual-eval token change? // maybe it is single-eval or dual/three-eval token change?
std::vector<CPubKey> vinPubkeys; std::vector<CPubKey> vinPubkeys;
ExtractTokensVinPubkeys(tx, vinPubkeys); ExtractTokensVinPubkeys(tx, vinPubkeys);
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) {
CTxOut testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it); testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk")));
CTxOut testDualVout1 = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, *it); testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { if (evalCode2 != 0)
//std::cerr << indentStr << "IsTokensvout() this is single-eval token change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; // also check in backward evalcode order:
return tx.vout[v].nValue; testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
}
if (tx.vout[v].scriptPubKey == testDualVout1.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token change, vout eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
} }
// try all test vouts:
for (auto t : testVouts) {
if (t.first == tx.vout[v]) {
LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
return tx.vout[v].nValue;
}
}
LOGSTREAM("cctokens", 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 { 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("cctokens", 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);
return tx.vout[v].nValue; return tx.vout[v].nValue;
} }
} }
@@ -487,7 +563,7 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
} }
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) // compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid)
{ {
CTransaction vinTx; CTransaction vinTx;
uint256 hashBlock; uint256 hashBlock;
@@ -503,6 +579,8 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu
// this is just for log messages indentation for debugging recursive calls: // this is just for log messages indentation for debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.'); std::string indentStr = std::string().append(tokenValIndentSize, '.');
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
for (int32_t i = 0; i<numvins; i++) for (int32_t i = 0; i<numvins; i++)
{ // check for additional contracts which may send tokens to the Tokens contract { // 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*/) if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/)
@@ -511,37 +589,38 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu
// we are not inside the validation code -- dimxy // 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))) if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{ {
std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl);
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
} }
else { else {
tokenValIndentSize++; LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl);
// validate vouts of vintx
//std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; // validate vouts of vintx
tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, tokenid); tokenValIndentSize++;
tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid);
tokenValIndentSize--; tokenValIndentSize--;
if (tokenoshis != 0) if (tokenoshis != 0)
{ {
std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl);
inputs += tokenoshis; inputs += tokenoshis;
} }
} }
} }
} }
for (int32_t i = 0; i < numvouts-1; i ++) // 'numvouts-1' <-- do not check opret
for (int32_t i = 0; i<numvouts; i++)
{ {
tokenValIndentSize++; LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl);
// Note: we pass in here 'false' because we don't need to call TokenExactAmounts() recursively from IsTokensvout
// indeed, in this case we'll be checking this tx again // Note: we pass in here IsTokenvout(false,...) because we don't need to call TokenExactAmounts() recursively from IsTokensvout here
//std::cerr << indentStr << "TokenExactAmounts() check vout i=" << i << " nValue=" << tx.vout[i].nValue << std::endl; // indeed, if we pass 'true' we'll be checking this tx vout again
tokenoshis = IsTokensvout(false, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, tokenid); tokenValIndentSize++;
tokenoshis = IsTokensvout(false /*<--do not recursion here*/, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, reftokenid);
tokenValIndentSize--; tokenValIndentSize--;
if (tokenoshis != 0) if (tokenoshis != 0)
{ {
std::cerr << indentStr << "TokensExactAmounts() vout i=" << i << " tokenoshis=" << tokenoshis << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl);
outputs += tokenoshis; outputs += tokenoshis;
} }
} }
@@ -549,149 +628,226 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu
//std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; //std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
if (inputs != outputs) { if (inputs != outputs) {
if (tx.GetHash() != tokenid) if (tx.GetHash() != reftokenid)
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; LOGSTREAM("cctokens", 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);
return false; // do not call eval->Invalid() here! return false; // do not call eval->Invalid() here!
} }
else else
return true; return true;
} }
// add inputs from token cc addr // get non-fungible data from 'tokenbase' tx (the data might be empty)
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) void GetNonfungibleData(uint256 tokenid, std::vector<uint8_t> &vopretNonfungible)
{
CTransaction tokenbasetx;
uint256 hashBlock;
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
LOGSTREAM("cctokens", CCLOG_INFO, stream << "SetNonfungibleEvalCode() cound not load token creation tx=" << tokenid.GetHex() << std::endl);
return;
}
vopretNonfungible.clear();
// check if it is non-fungible tx and get its second evalcode from non-fungible payload
if (tokenbasetx.vout.size() > 0) {
uint8_t dummyEvalCode;
uint256 tokenIdOpret;
std::vector<CPubKey> voutPubkeys;
std::vector<uint8_t> vopretExtra;
DecodeTokenOpRet(tokenbasetx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretNonfungible, vopretExtra);
}
}
// overload, adds inputs from token cc addr
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) {
std::vector<uint8_t> vopretNonfungibleDummy;
return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy);
}
// adds inputs from token cc addr and returns non-fungible opret payload if present
// also sets evalcode in cp, if needed
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, std::vector<uint8_t> &vopretNonfungible)
{ {
char tokenaddr[64], destaddr[64]; char tokenaddr[64], destaddr[64];
int64_t threshold, nValue, price, totalinputs = 0; int64_t threshold, nValue, price, totalinputs = 0;
uint256 txid, hashBlock; int32_t n = 0;
//std::vector<uint8_t> vopretExtra;
CTransaction vintx;
int32_t j, vout, n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetNonfungibleData(tokenid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
GetTokensCCaddress(cp, tokenaddr, pk); GetTokensCCaddress(cp, tokenaddr, pk);
SetCCunspents(unspentOutputs, tokenaddr); SetCCunspents(unspentOutputs, tokenaddr);
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("cctokens", 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
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
{ {
txid = it->first.txhash; CTransaction vintx;
vout = (int32_t)it->first.index; uint256 hashBlock;
if (it->second.satoshis < threshold) uint256 vintxid = it->first.txhash;
continue; int32_t vout = (int32_t)it->first.index;
for (j = 0; j<mtx.vin.size(); j++)
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n) if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue)
break;
if (j != mtx.vin.size())
continue; continue;
if (GetTransaction(txid, vintx, hashBlock, false) != 0) int32_t ivin;
for (ivin = 0; ivin < mtx.vin.size(); ivin ++)
if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n)
break;
if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls)
continue;
if (GetTransaction(vintxid, vintx, hashBlock, false) != 0)
{ {
Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey);
if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0) if (strcmp(destaddr, tokenaddr) != 0 &&
strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used?
strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)?
continue; continue;
//fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN);
std::vector<CPubKey> vinPubkeys;
if ((nValue = IsTokensvout(true, true/*<--add only checked token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl);
if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(vintxid, vout) == 0)
{ {
if (total != 0 && maxinputs != 0) //for non-fungible tokens check payload:
mtx.vin.push_back(CTxIn(txid, vout, CScript())); if (!vopretNonfungible.empty()) {
std::vector<uint8_t> vopret;
// check if it is non-fungible token:
GetNonfungibleData(tokenid, vopret);
if (vopret != vopretNonfungible) {
LOGSTREAM("cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl);
continue;
}
// non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1
}
if (total != 0 && maxinputs != 0) // if it is not just to calc amount...
mtx.vin.push_back(CTxIn(vintxid, vout, CScript()));
nValue = it->second.satoshis; nValue = it->second.satoshis;
totalinputs += nValue; totalinputs += nValue;
std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl; LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl);
n++; n++;
if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs))
break; break;
} }
} }
} }
//std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl; //std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl;
return(totalinputs); return(totalinputs);
} }
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description) std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, std::vector<uint8_t> nonfungibleData)
{ {
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp, C; CPubKey mypk; struct CCcontract_info *cp, C;
if (assetsupply < 0) if (tokensupply < 0)
{ {
fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply); LOGSTREAM("cctokens", CCLOG_INFO, stream << "CreateToken() negative tokensupply=" << tokensupply << std::endl);
return(""); return std::string("");
} }
if (!nonfungibleData.empty() && tokensupply != 1) {
LOGSTREAM("cctokens", CCLOG_INFO, stream << "CreateToken() for non-fungible tokens tokensupply should be equal to 1" << std::endl);
CCerror = "for non-fungible tokens tokensupply should be equal to 1";
return std::string("");
}
cp = CCinit(&C, EVAL_TOKENS); cp = CCinit(&C, EVAL_TOKENS);
if (name.size() > 32 || description.size() > 4096) if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level
{ {
fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size()); LOGSTREAM("cctokens", CCLOG_INFO, stream << "name of=" << name.size() << " or description of=" << description.size() << " is too big" << std::endl);
CCerror = "name should be < 32, description should be < 4096";
return(""); return("");
} }
if (txfee == 0) if (txfee == 0)
txfee = 10000; txfee = 10000;
mypk = pubkey2pk(Mypubkey()); mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0) if (AddNormalinputs(mtx, mypk, tokensupply + 2 * txfee, 64) > 0)
{ {
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk)); uint8_t destEvalCode = EVAL_TOKENS;
if( nonfungibleData.size() > 0 )
destEvalCode = nonfungibleData.begin()[0];
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk));
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG));
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description))); return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData)));
} }
return("");
LOGSTREAM("cctokens", CCLOG_INFO, stream << "cant find normal inputs" << std::endl);
CCerror = "cant find normal inputs";
return std::string("");
} }
// transfer tokens to another pubkey
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total) // param additionalEvalCode allows transfer of dual-eval non-fungible tokens
std::string TokenTransfer(int64_t txfee, uint256 tokenid, std::vector<uint8_t> destpubkey, int64_t total)
{ {
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C;
std::vector<uint8_t> emptyExtraOpret; std::vector<uint8_t> vopretNonfungible;
if (total < 0) if (total < 0) {
{ LOGSTREAM("cctokens", CCLOG_INFO, stream << "negative total=" << total << std::endl);
fprintf(stderr, "negative total %lld\n", (long long)total);
return(""); return("");
} }
cp = CCinit(&C, EVAL_TOKENS); cp = CCinit(&C, EVAL_TOKENS);
if (txfee == 0) if (txfee == 0)
txfee = 10000; txfee = 10000;
mypk = pubkey2pk(Mypubkey()); mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{ {
//n = outputs.size(); mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore
//if ( n == amounts.size() )
//{ if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx!
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, total, 60)) > 0)
{ {
if (inputs < total) { //added dimxy if (inputs < total) { //added dimxy
std::cerr << "AssetTransfer(): insufficient funds" << std::endl; LOGSTREAM("cctokens", CCLOG_INFO, stream << "TokenTransfer(): insufficient token funds" << std::endl);
return (""); CCerror = strprintf("insufficient token inputs");
return std::string("");
} }
uint8_t destEvalCode = EVAL_TOKENS;
if (vopretNonfungible.size() > 0)
destEvalCode = vopretNonfungible.begin()[0];
if (inputs > total) if (inputs > total)
CCchange = (inputs - total); CCchange = (inputs - total);
//for (i=0; i<n; i++) mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, pubkey2pk(destpubkey))); // if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, total, pubkey2pk(destpubkey))); // TODO: or MakeTokensCC1vout??
if (CCchange != 0) if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk));
std::vector<CPubKey> voutTokenPubkeys; std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout 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 return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, vopretNonfungible, CScript())));
} }
else { else {
fprintf(stderr, "not enough CC token inputs for %.8f\n", (double)total / COIN); LOGSTREAM("cctokens", CCLOG_INFO, stream << "not enough CC token inputs for amount=" << total << std::endl);
CCerror = strprintf("no token inputs");
} }
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size()); //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
} }
else { else {
fprintf(stderr, "not enough normal inputs for txfee\n"); LOGSTREAM("cctokens", CCLOG_INFO, stream << "not enough normal inputs for txfee" << std::endl);
CCerror = strprintf("insufficient normal inputs");
} }
return(""); return("");
} }
@@ -708,7 +864,7 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid)
if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0) if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0)
{ {
fprintf(stderr, "cant find tokenid\n"); LOGSTREAM("cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl);
CCerror = strprintf("cant find tokenid"); CCerror = strprintf("cant find tokenid");
return 0; return 0;
} }
@@ -720,26 +876,35 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid)
UniValue TokenInfo(uint256 tokenid) UniValue TokenInfo(uint256 tokenid)
{ {
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name, description; char str[67], numstr[65]; UniValue result(UniValue::VOBJ);
uint256 hashBlock;
CTransaction vintx;
std::vector<uint8_t> origpubkey;
std::vector<uint8_t> vopretNonfungible;
std::string name, description;
if (GetTransaction(tokenid, vintx, hashBlock, false) == 0) if (GetTransaction(tokenid, vintx, hashBlock, false) == 0)
{ {
fprintf(stderr, "cant find assetid\n"); fprintf(stderr, "TokenInfo() cant find tokenid\n");
result.push_back(Pair("result", "error")); result.push_back(Pair("result", "error"));
result.push_back(Pair("error", "cant find tokenid")); result.push_back(Pair("error", "cant find tokenid"));
return(result); return(result);
} }
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) == 0) if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description, vopretNonfungible) == 0)
{ {
fprintf(stderr, "assetid isnt token creation txid\n"); LOGSTREAM("cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl);
result.push_back(Pair("result", "error")); result.push_back(Pair("result", "error"));
result.push_back(Pair("error", "assetid isnt token creation txid")); result.push_back(Pair("error", "tokenid isnt token creation txid"));
} }
result.push_back(Pair("result", "success")); result.push_back(Pair("result", "success"));
result.push_back(Pair("tokenid", uint256_str(str, tokenid))); result.push_back(Pair("tokenid", tokenid.GetHex()));
result.push_back(Pair("owner", pubkey33_str(str, origpubkey.data()))); result.push_back(Pair("owner", HexStr(origpubkey)));
result.push_back(Pair("name", name)); result.push_back(Pair("name", name));
result.push_back(Pair("supply", vintx.vout[0].nValue)); result.push_back(Pair("supply", vintx.vout[0].nValue));
result.push_back(Pair("description", description)); result.push_back(Pair("description", description));
if( !vopretNonfungible.empty() )
result.push_back(Pair("data", HexStr(vopretNonfungible)));
return(result); return(result);
} }

View File

@@ -28,14 +28,15 @@
// CCcustom // CCcustom
bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
bool TokensExactAmounts(bool goDeeper, 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);
//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector<CPubKey> vinPubkeys); std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector<uint8_t> nonfungibleData);
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<uint8_t> destpubkey, int64_t total); std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total);
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); int64_t GetTokenBalance(CPubKey pk, uint256 tokenid);
UniValue TokenInfo(uint256 tokenid); UniValue TokenInfo(uint256 tokenid);
UniValue TokenList(); UniValue TokenList();
void GetNonfungibleData(uint256 tokenid, std::vector<uint8_t> &vopretNonfungible);
//this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); //this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description); //this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);

View File

@@ -46,8 +46,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0;
int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0; int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0;
int32_t i,flag,utxovout,n,err = 0; int32_t i,flag,utxovout,n,err = 0;
char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], tokensunspendable[64]; char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64];
uint8_t *privkey, myprivkey[32], unspendablepriv[32], tokensunspendablepriv[32], *msg32 = 0; uint8_t *privkey, myprivkey[32], unspendablepriv[32], /*tokensunspendablepriv[32],*/ *msg32 = 0;
CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL;
CPubKey unspendablepk /*, tokensunspendablepk*/; CPubKey unspendablepk /*, tokensunspendablepk*/;
struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpTokens, tokensC;
@@ -69,26 +69,26 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
GetCCaddress(cp,myaddr,mypk); GetCCaddress(cp,myaddr,mypk);
mycond = MakeCCcond1(cp->evalcode,mypk); mycond = MakeCCcond1(cp->evalcode,mypk);
// to spend from single-eval evalcode 'unspendable' // to spend from single-eval evalcode 'unspendable' cc addr
unspendablepk = GetUnspendable(cp, unspendablepriv); unspendablepk = GetUnspendable(cp, unspendablepriv);
GetCCaddress(cp, unspendable, unspendablepk); GetCCaddress(cp, unspendable, unspendablepk);
othercond = MakeCCcond1(cp->evalcode, unspendablepk); othercond = MakeCCcond1(cp->evalcode, unspendablepk);
//printf("evalcode.%d (%s)\n",cp->evalcode,unspendable); //printf("evalcode.%d (%s)\n",cp->evalcode,unspendable);
// tokens support:
// to spend from dual-eval mypk vout // tokens support:
// to spend from dual/three-eval mypk vout
GetTokensCCaddress(cp, mytokensaddr, mypk); GetTokensCCaddress(cp, mytokensaddr, mypk);
mytokenscond = MakeTokensCCcond1(cp->evalcode, mypk); // NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond:
mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, mypk);
// to spend from single-eval EVAL_TOKENS mypk // to spend from single-eval EVAL_TOKENS mypk
cpTokens = CCinit(&tokensC, EVAL_TOKENS); cpTokens = CCinit(&tokensC, EVAL_TOKENS);
GetCCaddress(cpTokens, mysingletokensaddr, mypk); GetCCaddress(cpTokens, mysingletokensaddr, mypk);
mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk); mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk);
// to spend from dual-eval EVAL_TOKEN+evalcode 'unspendable' pk // to spend from dual/three-eval EVAL_TOKEN+evalcode 'unspendable' pk:
//tokensunspendablepk = GetUnspendable(cpTokens, tokensunspendablepriv); GetTokensCCaddress(cp, unspendabletokensaddr, unspendablepk); // it may be a three-eval cc, if cp->additionalEvalcode2 is set
GetTokensCCaddress(cp, tokensunspendable, unspendablepk); othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, unspendablepk);
othertokenscond = MakeTokensCCcond1(cp->evalcode, unspendablepk);
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end //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. //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.
@@ -155,9 +155,10 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
{ {
Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey); Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey);
//fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr); //fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr);
//std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " myaddr=" << myaddr << std::endl; std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl;
if( strcmp(destaddr,myaddr) == 0 ) if( strcmp(destaddr, myaddr) == 0 )
{ {
fprintf(stderr, "FinalizeCCTx() matched cc myaddr (%s)\n", myaddr);
privkey = myprivkey; privkey = myprivkey;
cond = mycond; cond = mycond;
} }
@@ -165,48 +166,48 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
{ {
privkey = myprivkey; privkey = myprivkey;
cond = mytokenscond; cond = mytokenscond;
//fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout CC addr.(%s)\n",mytokensaddr); fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout my token addr.(%s)\n",mytokensaddr);
} }
else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout
{ {
privkey = myprivkey; privkey = myprivkey;
cond = mysingletokenscond; cond = mysingletokenscond;
//fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout CC addr.(%s)\n", mytokensaddr); fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout my token addr.(%s)\n", mytokensaddr);
} }
else if ( strcmp(destaddr,unspendable) == 0 ) else if ( strcmp(destaddr,unspendable) == 0 )
{ {
privkey = unspendablepriv; privkey = unspendablepriv;
cond = othercond; cond = othercond;
//fprintf(stderr,"FinalizeCCTx(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable);
} }
else if (strcmp(destaddr, tokensunspendable) == 0) else if (strcmp(destaddr, unspendabletokensaddr) == 0)
{ {
privkey = unspendablepriv; privkey = unspendablepriv;
cond = othertokenscond; cond = othertokenscond;
//fprintf(stderr,"FinalizeCCTx() matched tokensunspendable CC addr.(%s)\n",unspendable); fprintf(stderr,"FinalizeCCTx() matched unspendabletokensaddr dual/three-eval CC addr.(%s)\n",unspendabletokensaddr);
} }
// check if this is the 2nd additional evalcode + 'unspendable' cc addr: // check if this is the 2nd additional evalcode + 'unspendable' cc addr:
else if ( strcmp(destaddr,cp->unspendableaddr2) == 0) else if ( strcmp(destaddr, cp->unspendableaddr2) == 0)
{ {
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2); fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2);
privkey = cp->unspendablepriv2; privkey = cp->unspendablepriv2;
if ( othercond2 == 0 ) if( othercond2 == 0 )
othercond2 = MakeCCcond1(cp->evalcode2, cp->unspendablepk2); othercond2 = MakeCCcond1(cp->unspendableEvalcode2, cp->unspendablepk2);
cond = othercond2; cond = othercond2;
} }
// check if this is 3rd additional evalcode + 'unspendable' cc addr: // check if this is 3rd additional evalcode + 'unspendable' cc addr:
else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 ) else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 )
{ {
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3); fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3);
privkey = cp->unspendablepriv3; privkey = cp->unspendablepriv3;
if ( othercond3 == 0 ) if( othercond3 == 0 )
othercond3 = MakeCCcond1(cp->evalcode3,cp->unspendablepk3); othercond3 = MakeCCcond1(cp->unspendableEvalcode3, cp->unspendablepk3);
cond = othercond3; cond = othercond3;
} }
// check if this is spending from 1of2 cc coins addr: // check if this is spending from 1of2 cc coins addr:
else if (strcmp(cp->coins1of2addr, destaddr) == 0) else if (strcmp(cp->coins1of2addr, destaddr) == 0)
{ {
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr);
privkey = myprivkey; privkey = myprivkey;
if (othercond1of2 == 0) if (othercond1of2 == 0)
othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]); othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]);
@@ -215,10 +216,12 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
// check if this is spending from 1of2 cc tokens addr: // check if this is spending from 1of2 cc tokens addr:
else if (strcmp(cp->tokens1of2addr, destaddr) == 0) else if (strcmp(cp->tokens1of2addr, destaddr) == 0)
{ {
//fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr);
privkey = myprivkey; privkey = myprivkey;
if (othercond1of2tokens == 0) if (othercond1of2tokens == 0)
othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc
// TODO: verify evalcodes order if additionalEvalcode2 is not 0
othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, cp->tokens1of2pk[0], cp->tokens1of2pk[1]);
cond = othercond1of2tokens; cond = othercond1of2tokens;
} }
else else

View File

@@ -76,7 +76,8 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2)
return(vout); return(vout);
} }
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) // make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
{ {
// make 1of2 sigs cond // make 1of2 sigs cond
std::vector<CC*> pks; std::vector<CC*> pks;
@@ -85,44 +86,68 @@ CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2)
std::vector<CC*> thresholds; std::vector<CC*> thresholds;
thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) ); thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) );
if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc if( evalcode2 != 0 )
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
return CCNewThreshold(thresholds.size(), thresholds); return CCNewThreshold(thresholds.size(), thresholds);
} }
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
}
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) // make three-eval (token+evalcode+evalcode2) cryptocondition:
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
{ {
std::vector<CC*> pks; std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk)); pks.push_back(CCNewSecp256k1(pk));
std::vector<CC*> thresholds; std::vector<CC*> thresholds;
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
thresholds.push_back(CCNewThreshold(1, pks)); // signature if (evalcode2 != 0)
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
thresholds.push_back(CCNewThreshold(1, pks)); // signature
return CCNewThreshold(thresholds.size(), thresholds); return CCNewThreshold(thresholds.size(), thresholds);
} }
// overload to make two-eval (token+evalcode) cryptocondition:
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
return MakeTokensCCcond1(evalcode, 0, pk);
}
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) // make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
{ {
CTxOut vout; CTxOut vout;
CC *payoutCond = MakeTokensCCcond1of2(evalcode, pk1, pk2); CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
vout = CTxOut(nValue, CCPubKey(payoutCond)); vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond); cc_free(payoutCond);
return(vout); return(vout);
} }
// overload to make two-eval (token+evalcode) 1of2 cc vout:
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
}
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) // make three-eval (token+evalcode+evalcode2) cc vout:
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
{ {
CTxOut vout; CTxOut vout;
CC *payoutCond = MakeTokensCCcond1(evalcode, pk); CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
vout = CTxOut(nValue, CCPubKey(payoutCond)); vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond); cc_free(payoutCond);
return(vout); return(vout);
} }
// overload to make two-eval (token+evalcode) cc vout:
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
}
CC* GetCryptoCondition(CScript const& scriptSig) CC* GetCryptoCondition(CScript const& scriptSig)
{ {
@@ -227,17 +252,19 @@ CPubKey pubkey2pk(std::vector<uint8_t> pubkey)
return(pk); return(pk);
} }
// set additional 'unspendable' addr
void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr)
{ {
cp->evalcode2 = evalcode; cp->unspendableEvalcode2 = evalcode;
cp->unspendablepk2 = pk; cp->unspendablepk2 = pk;
memcpy(cp->unspendablepriv2,priv,32); memcpy(cp->unspendablepriv2,priv,32);
strcpy(cp->unspendableaddr2,coinaddr); strcpy(cp->unspendableaddr2,coinaddr);
} }
// set yet another additional 'unspendable' addr
void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr)
{ {
cp->evalcode3 = evalcode; cp->unspendableEvalcode3 = evalcode;
cp->unspendablepk3 = pk; cp->unspendablepk3 = pk;
memcpy(cp->unspendablepriv3,priv,32); memcpy(cp->unspendablepriv3,priv,32);
strcpy(cp->unspendableaddr3,coinaddr); strcpy(cp->unspendableaddr3,coinaddr);
@@ -251,12 +278,13 @@ void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *c
strcpy(cp->coins1of2addr, coinaddr); strcpy(cp->coins1of2addr, coinaddr);
} }
// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 tokens cryptocondition vout: // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 token cryptocondition vout
void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr) // to get tokenaddr use GetTokensCCaddress()
void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *tokenaddr)
{ {
cp->tokens1of2pk[0] = pk1; cp->tokens1of2pk[0] = pk1;
cp->tokens1of2pk[1] = pk2; cp->tokens1of2pk[1] = pk2;
strcpy(cp->tokens1of2addr, coinaddr); strcpy(cp->tokens1of2addr, tokenaddr);
} }
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
@@ -351,11 +379,11 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk)
return(_GetCCaddress(destaddr,cp->evalcode,pk)); return(_GetCCaddress(destaddr,cp->evalcode,pk));
} }
bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk) bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
{ {
CC *payoutCond; CC *payoutCond;
destaddr[0] = 0; destaddr[0] = 0;
if ((payoutCond = MakeTokensCCcond1(evalcode, pk)) != 0) if ((payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk)) != 0)
{ {
Getscriptaddress(destaddr, CCPubKey(payoutCond)); Getscriptaddress(destaddr, CCPubKey(payoutCond));
cc_free(payoutCond); cc_free(payoutCond);
@@ -363,12 +391,13 @@ bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk)
return(destaddr[0] != 0); return(destaddr[0] != 0);
} }
// get scriptPubKey adddress for three/dual eval token cc vout
bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk) bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk)
{ {
destaddr[0] = 0; destaddr[0] = 0;
if (pk.size() == 0) if (pk.size() == 0)
pk = GetUnspendable(cp, 0); pk = GetUnspendable(cp, 0);
return(_GetTokensCCaddress(destaddr, cp->evalcode, pk)); return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->additionalTokensEvalcode2, pk));
} }
@@ -384,11 +413,12 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK
return(destaddr[0] != 0); return(destaddr[0] != 0);
} }
// get scriptPubKey adddress for three/dual eval token 1of2 cc vout
bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2) bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2)
{ {
CC *payoutCond; CC *payoutCond;
destaddr[0] = 0; destaddr[0] = 0;
if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, pk, pk2)) != 0) if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc
{ {
Getscriptaddress(destaddr, CCPubKey(payoutCond)); Getscriptaddress(destaddr, CCPubKey(payoutCond));
cc_free(payoutCond); cc_free(payoutCond);
@@ -515,7 +545,7 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv)
void CCclearvars(struct CCcontract_info *cp) void CCclearvars(struct CCcontract_info *cp)
{ {
cp->evalcode2 = cp->evalcode3 = 0; cp->unspendableEvalcode2 = cp->unspendableEvalcode3 = 0;
cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0; cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0;
} }

View File

@@ -14,6 +14,7 @@
******************************************************************************/ ******************************************************************************/
#include "CCassets.h" #include "CCassets.h"
#include "CCtokens.h"
/* /*
Assets can be created or transferred. Assets can be created or transferred.
@@ -129,21 +130,21 @@
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn) bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
{ {
static uint256 zero; static uint256 zero;
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; CTxDestination address;
int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; CTransaction vinTx, createTx;
int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey; uint256 hashBlock, assetid, assetid2;
int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore;
std::vector<uint8_t> origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy;
uint8_t funcid, evalCodeInOpret; uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], assetsCCaddr[64], userTokensCCaddr[64]; //, signleEvalTokensCCaddr[64]; char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64];
char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64];
//return true; //return true;
//CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL);
//CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, NULL);
//GetCCaddress(cpTokens, tokensUnspendableCCaddr, unspendableTokensPk);
numvins = tx.vin.size(); numvins = tx.vin.size();
numvouts = tx.vout.size(); numvouts = tx.vout.size();
outputs = inputs = 0; outputsDummy = inputs = 0;
preventCCvins = preventCCvouts = -1; preventCCvins = preventCCvouts = -1;
// add specific chains exceptions for old token support: // add specific chains exceptions for old token support:
@@ -156,13 +157,18 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
if (numvouts == 0) if (numvouts == 0)
return eval->Invalid("AssetValidate: no vouts"); return eval->Invalid("AssetValidate: no vouts");
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey, vopretNonfungibleDummy)) == 0 )
return eval->Invalid("AssetValidate: invalid opreturn payload"); return eval->Invalid("AssetValidate: invalid opreturn payload");
// non-fungible tokens support:
GetNonfungibleData(assetid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
// find dual-eval tokens unspendable addr: // find dual-eval tokens unspendable addr:
char tokensUnspendableAddr[64],origpubkeyCCaddr[64]; GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL));
GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); // this is for marker validation:
GetCCaddress(cpAssets, origpubkeyCCaddr, origpubkey); GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey);
// we need this for validating single-eval tokens' vins/vous: // we need this for validating single-eval tokens' vins/vous:
struct CCcontract_info *cpTokens, tokensC; struct CCcontract_info *cpTokens, tokensC;
@@ -177,7 +183,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
return eval->Invalid("cant find asset create txid"); return eval->Invalid("cant find asset create txid");
else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset2 create txid"); return eval->Invalid("cant find asset2 create txid");
else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin
return eval->Invalid("illegal asset vin0"); return eval->Invalid("illegal asset vin0");
else if( numvouts < 2 ) else if( numvouts < 2 )
return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below
@@ -191,7 +197,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
if( assetid == zero ) if( assetid == zero )
return eval->Invalid("illegal assetid"); return eval->Invalid("illegal assetid");
else if (!AssetCalcAmounts(cpAssets, inputs, outputs, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs else if (!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
return false; // returns false if some problems with reading vintxes return false; // returns false if some problems with reading vintxes
} }
} }
@@ -226,9 +232,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.1: CC output for marker //vout.1: CC output for marker
//vout.2: normal output for change (if any) //vout.2: normal output for change (if any)
// vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
// as we don't use tokenconvert we should not be here:
return eval->Invalid("invalid asset funcid (b)");
if( remaining_price == 0 ) if( remaining_price == 0 )
return eval->Invalid("illegal null amount for buyoffer"); return eval->Invalid("illegal null amount for buyoffer");
else if( ConstrainVout(tx.vout[0],1,cpAssets->unspendableCCaddr,0) == 0 ) else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr
return eval->Invalid("invalid vout for buyoffer"); return eval->Invalid("invalid vout for buyoffer");
preventCCvins = 1; preventCCvins = 1;
preventCCvouts = 1; preventCCvouts = 1;
@@ -243,13 +253,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.1: vin.2 back to users pubkey //vout.1: vin.2 back to users pubkey
//vout.2: normal output for change (if any) //vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['o'] //vout.n-1: opreturn [EVAL_ASSETS] ['o']
if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 )
return(false); return(false);
else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 ) else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 )
return eval->Invalid("invalid refund for cancelbuy"); return eval->Invalid("invalid refund for cancelbuy");
preventCCvins = 3; preventCCvins = 3;
preventCCvouts = 0; preventCCvouts = 0;
fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr); fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr);
break; break;
case 'B': // fillbuy: case 'B': // fillbuy:
@@ -264,7 +274,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
preventCCvouts = 4; preventCCvouts = 4;
if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false); return(false);
else if( numvouts < 4 ) else if( numvouts < 4 )
return eval->Invalid("not enough vouts for fillbuy"); return eval->Invalid("not enough vouts for fillbuy");
@@ -274,24 +284,24 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
{ {
if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) if( nValue != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); return eval->Invalid("locked value doesnt match vout0+1 fillbuy");
else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present
{ {
if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, 0) == 0 ) // tokens on user cc addr if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); return eval->Invalid("vout2 doesnt go to origpubkey fillbuy");
else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue )
return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy");
} }
else if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, inputs) == 0 ) // tokens on user cc addr else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
return eval->Invalid("vout2 doesnt match inputs fillbuy"); return eval->Invalid("vout2 doesnt match inputs fillbuy");
else if( ConstrainVout(tx.vout[1],0,0,0) == 0 ) else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 )
return eval->Invalid("vout1 is CC for fillbuy"); return eval->Invalid("vout1 is CC for fillbuy");
else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr
return eval->Invalid("invalid marker for original pubkey"); return eval->Invalid("invalid marker for original pubkey");
else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillbuy"); return eval->Invalid("mismatched remainder for fillbuy");
else if( remaining_price != 0 ) else if( remaining_price != 0 )
{ {
if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins on asset unspendable cc addr if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
} }
} }
@@ -308,25 +318,30 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.3: normal output for change (if any) //vout.3: normal output for change (if any)
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
// as we don't use tokenconvert we should not be here:
return eval->Invalid("invalid asset funcid (s)");
preventCCvouts = 2; preventCCvouts = 2;
if( remaining_price == 0 ) if( remaining_price == 0 )
return eval->Invalid("illegal null remaining_price for selloffer"); return eval->Invalid("illegal null remaining_price for selloffer");
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout1 for sellvin"); return eval->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++; 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!
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs ) else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs )
return eval->Invalid("mismatched vout0+vout2 total for selloffer"); return eval->Invalid("mismatched vout0+vout2 total for selloffer");
} }
else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // no cc change, just vout[0] // no cc change:
else if( 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!
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
break; break;
case 'x': // cancel case 'x': // cancel sell
//vin.0: normal input //vin.0: normal input
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
//vin.2: CC marker from selloffer for txfee //vin.2: CC marker from selloffer for txfee
@@ -335,9 +350,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.2: normal output for change (if any) //vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) // NOTE: if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE:
return(false); return(false);
else if( ConstrainVout(tx.vout[0], 1, userTokensCCaddr, assetoshis) == 0 ) else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr
return eval->Invalid("invalid vout for cancel"); return eval->Invalid("invalid vout for cancel");
preventCCvins = 3; preventCCvins = 3;
preventCCvouts = 1; preventCCvouts = 1;
@@ -353,7 +368,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
//vout.3: normal output for change (if any) //vout.3: normal output for change (if any)
//'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false); return(false);
else if( numvouts < 4 ) else if( numvouts < 4 )
return eval->Invalid("not enough vouts for fillask"); return eval->Invalid("not enough vouts for fillask");
@@ -365,17 +380,15 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
return eval->Invalid("locked value doesnt match vout0+1 fillask"); return eval->Invalid("locked value doesnt match vout0+1 fillask");
if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillask"); return eval->Invalid("mismatched remainder for fillask");
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 ) else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr
return eval->Invalid("normal vout1 for fillask"); return eval->Invalid("normal vout1 for fillask");
else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 ) else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr
return eval->Invalid("normal vout1 for fillask"); return eval->Invalid("normal vout1 for fillask");
else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr
return eval->Invalid("invalid marker for original pubkey"); return eval->Invalid("invalid marker for original pubkey");
else if( remaining_price != 0 ) else if( remaining_price != 0 )
{ {
//char tokensUnspendableAddr[64]; if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 )
//GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
if ( ConstrainVout(tx.vout[0], 1, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/, 0) == 0 )
return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell"); return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell");
} }
} }
@@ -399,7 +412,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
// eval->Invalid("asset2 inputs != outputs"); // eval->Invalid("asset2 inputs != outputs");
////////// not implemented yet //////////// ////////// not implemented yet ////////////
if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false); return(false);
else if( numvouts < 3 ) else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillex"); return eval->Invalid("not enough vouts for fillex");
@@ -412,7 +425,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
////////// not implemented yet //////////// ////////// not implemented yet ////////////
{ {
if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, 0) == 0 ) if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 )
return eval->Invalid("vout2 doesnt go to origpubkey fillex"); return eval->Invalid("vout2 doesnt go to origpubkey fillex");
else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
{ {
@@ -421,7 +434,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
} }
} }
////////// not implemented yet //////////// ////////// not implemented yet ////////////
else if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, inputs) == 0 ) else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 )
return eval->Invalid("vout2 doesnt match inputs fillex"); return eval->Invalid("vout2 doesnt match inputs fillex");
else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
return eval->Invalid("vout1 is CC for fillex"); return eval->Invalid("vout1 is CC for fillex");

View File

@@ -171,7 +171,7 @@ CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp)
if ( ctx == 0 ) if ( ctx == 0 )
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
Myprivkey(priv); Myprivkey(priv);
cp->evalcode2 = EVAL_ORACLES; cp->unspendableEvalcode2 = EVAL_ORACLES;
for (i=0; i<32; i++) for (i=0; i<32; i++)
cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]);
while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 )

View File

@@ -13,6 +13,7 @@
* * * *
******************************************************************************/ ******************************************************************************/
#include "CCassets.h"
#include "CCPrices.h" #include "CCPrices.h"
/* /*
@@ -89,6 +90,9 @@ uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &o
bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{ {
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
return true; // TODO remove, for test dual-evals
return eval->Invalid("no validation yet"); return eval->Invalid("no validation yet");
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
numvins = tx.vin.size(); numvins = tx.vin.size();

View File

@@ -7038,75 +7038,98 @@ UniValue tokenbalance(const UniValue& params, bool fHelp)
UniValue tokencreate(const UniValue& params, bool fHelp) UniValue tokencreate(const UniValue& params, bool fHelp)
{ {
UniValue result(UniValue::VOBJ); std::string name,description,hex; uint64_t supply; UniValue result(UniValue::VOBJ);
if ( fHelp || params.size() > 3 || params.size() < 2 ) std::string name, description, hextx;
throw runtime_error("tokencreate name supply description\n"); std::vector<uint8_t> nonfungibleData;
int64_t supply; // 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 )
throw runtime_error("tokencreate name supply [description][data]\n");
if ( ensure_CCrequirements() < 0 ) 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"); 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; const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
name = params[0].get_str(); 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"); ERR_RESULT("Token name must not be empty and up to 32 characters");
return(result); 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"); ERR_RESULT("Token supply must be positive");
return(result); return(result);
} }
if ( params.size() == 3 )
{ if (params.size() >= 3) {
description = params[2].get_str(); description = params[2].get_str();
if ( description.size() > 4096 ) if (description.size() > 4096) {
{
ERR_RESULT("Token description must be <= 4096 characters"); ERR_RESULT("Token description must be <= 4096 characters");
return(result); return(result);
} }
} }
hex = CreateToken(0,supply,name,description);
if ( hex.size() > 0 ) if (params.size() == 4) {
{ nonfungibleData = ParseHex(params[3].get_str());
if (nonfungibleData.size() > 10000) // opret limit
{
ERR_RESULT("Non-fungible data must be <= 10000");
return(result);
}
}
hextx = CreateToken(0, supply, name, description, nonfungibleData);
if( hextx.size() > 0 ) {
result.push_back(Pair("result", "success")); result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex)); result.push_back(Pair("hex", hextx));
} else ERR_RESULT("couldnt create transaction"); }
else
ERR_RESULT(CCerror);
return(result); return(result);
} }
UniValue tokentransfer(const UniValue& params, bool fHelp) UniValue tokentransfer(const UniValue& params, bool fHelp)
{ {
UniValue result(UniValue::VOBJ); std::string hex; int64_t amount; uint256 tokenid; UniValue result(UniValue::VOBJ);
if ( fHelp || params.size() != 3 ) std::string hex;
int64_t amount;
uint256 tokenid;
CCerror.clear();
if ( fHelp || params.size() != 3)
throw runtime_error("tokentransfer tokenid destpubkey amount\n"); throw runtime_error("tokentransfer tokenid destpubkey amount\n");
if ( ensure_CCrequirements() < 0 ) 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"); 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; const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
tokenid = Parseuint256((char *)params[0].get_str().c_str()); tokenid = Parseuint256((char *)params[0].get_str().c_str());
std::vector<unsigned char> pubkey(ParseHex(params[1].get_str().c_str())); std::vector<unsigned char> 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 amount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance
if ( tokenid == zeroid ) if( tokenid == zeroid ) {
{
ERR_RESULT("invalid tokenid"); ERR_RESULT("invalid tokenid");
return(result); return(result);
} }
if ( amount <= 0 ) if( amount <= 0 ) {
{
ERR_RESULT("amount must be positive"); ERR_RESULT("amount must be positive");
return(result); return(result);
} }
hex = TokenTransfer(0,tokenid,pubkey,amount);
if (amount > 0) { hex = TokenTransfer(0, tokenid, pubkey, amount);
if ( hex.size() > 0 )
{ if( !CCerror.empty() ) {
result.push_back(Pair("result", "success")); ERR_RESULT(CCerror);
result.push_back(Pair("hex", hex)); }
} else ERR_RESULT("couldnt transfer assets"); else {
} else { result.push_back(Pair("result", "success"));
ERR_RESULT("amount must be positive"); result.push_back(Pair("hex", hex));
} }
return(result); return(result);
} }
@@ -7786,3 +7809,4 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp)
cp = CCinit(&C, EVAL_HEIR); cp = CCinit(&C, EVAL_HEIR);
return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret));
} }