Merge pull request #1238 from dimxy/nonfungible-opret-fix

Nonfungible opret fix
This commit is contained in:
jl777
2019-02-11 04:36:11 -11:00
committed by GitHub
8 changed files with 208 additions and 171 deletions

View File

@@ -30,7 +30,7 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
// CCassetsCore
CScript EncodeAssetOpRet(uint8_t assetFuncId, 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);
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
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);
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

@@ -279,9 +279,8 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &o
return(0);
} */
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)
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> vopret1, vopret2;
std::vector<uint8_t> vopretAssets; //, vopretAssetsStripped;
uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId;
uint256 dummyTokenid;
@@ -294,30 +293,9 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo
assetsFuncId = 0;
// First - decode token opret:
funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopret1, vopret2);
funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopretAssets);
LOGSTREAM((char *)"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);
script = (uint8_t *)vopret.data();
if (script == 0) {
std::cerr << "DecodeAssetOpRet() script is empty" << std::endl;
return (uint8_t)0;
}*/
//bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
//bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof());
if (funcId == 0 || vopretAssets.size() < 2) {
LOGSTREAM((char *)"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;
@@ -363,13 +341,12 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo
}
break;
default:
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() illegal assetFuncId=" << (int)funcId << std::endl);
break;
}
}
}
LOGSTREAM((char *)"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);
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl);
return (uint8_t)0;
}
@@ -378,9 +355,8 @@ bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CT
{
uint256 assetid,assetid2;
uint8_t evalCode;
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 )
if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 )
return(true);
else
return(false);
@@ -396,10 +372,9 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origN
std::vector<uint8_t> origpubkey;
CScript script;
uint8_t evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
n = vintx.vout.size();
if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy)) == 0 )
if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 )
return(false);
bool bGetCCaddr = false;
@@ -444,9 +419,8 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCadd
int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy);
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey);
}
if( tx.vin.size() < 2 )
@@ -495,7 +469,6 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCadd
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;
std::vector<uint8_t> vopretNonfungibleDummy;
CCaddr[0] = origaddr[0] = 0;
@@ -504,7 +477,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
return(0);
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout0 for buyvin");
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy)) == 'b' &&
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' &&
vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'?
return eval->Invalid("invalid normal vout1 for buyvin");
else
@@ -539,12 +512,11 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr
uint256 assetidOpret, assetidOpret2;
uint8_t funcid, evalCode;
std::vector<uint8_t> vopretNonfungibleDummy;
// this is just for log messages indentation fur debugging recursive calls:
int32_t n = tx.vout.size();
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey, vopretNonfungibleDummy)) == 0)
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0)
{
std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl;
return(false);

View File

@@ -76,18 +76,17 @@ UniValue AssetOrders(uint256 refassetid)
uint256 txid, hashBlock, assetid, assetid2;
int64_t price;
std::vector<uint8_t> origpubkey;
std::vector<uint8_t> vopretNonfungible;
CTransaction vintx;
uint8_t funcid, evalCode;
char numstr[32], funcidstr[16], origaddr[64], assetidstr[65];
txid = it->first.txhash;
//std::cerr << "addOrders() txid=" << txid.GetHex() << std::endl;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
if ( GetTransaction(txid, vintx, hashBlock, false) != 0 )
{
// 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;
if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungible)) != 0)
if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0)
{
if (refassetid != zero && assetid != refassetid)
{
@@ -402,7 +401,7 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
voutTokenPubkeys.push_back(unspendableAssetsPubkey);
return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())));
}
else {
@@ -506,11 +505,12 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
std::vector<uint8_t> vopretNonfungible;
GetNonfungibleData(assetid, vopretNonfungible);
bidamount = vintx.vout[0].nValue;
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, vopretNonfungible))!=0)
if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
{
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'
@@ -522,8 +522,8 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))));
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))));
}
}
return("");
@@ -555,10 +555,12 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
std::vector<uint8_t> vopretNonfungible;
GetNonfungibleData(assetid, vopretNonfungible);
askamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0)
if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
{
if (funcid == 's')
mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s'
@@ -588,7 +590,7 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))));
}
}
@@ -675,7 +677,7 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f
voutTokenPubkeys.push_back(pubkey2pk(origpubkey));
return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))));
} else return("dont have any assets to fill bid");
}
@@ -800,7 +802,7 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a
cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2;
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))));
} else {
CCerror = strprintf("filltx not enough utxos");

View File

@@ -67,6 +67,12 @@ one other technical note is that komodod has the insight-explorer extensions bui
#include "../komodo_cJSON.h"
// opret data block ids:
enum {
OPRETID_NONFUNGIBLEDATA = 0x11
// TODO: OPRETID_ASSETSDATA = 0x12
};
struct CC_utxo
{
uint256 txid;
@@ -173,11 +179,10 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
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, 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, 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> &vopret1, std::vector<uint8_t> &vopret2);
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 &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra);
void GetNonfungibleData(uint256 tokenid, std::vector<uint8_t> &vopretNonfungible);
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);
@@ -186,7 +191,7 @@ int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t
// CCcustom
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);
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);
//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);
// CCutils
bool priv2addr(char *coinaddr,uint8_t buf33[33],uint8_t priv32[32]);
@@ -262,7 +267,7 @@ UniValue ValueFromAmount(const CAmount& amount);
#define CCLOG_DEBUG2 2
#define CCLOG_DEBUG3 3
template <class T>
inline void CCLogPrintStream(char *category, int level, T print_to_stream)
inline void CCLogPrintStream(const char *category, int level, T print_to_stream)
{
std::ostringstream stream;
print_to_stream(stream);

View File

@@ -50,18 +50,15 @@ CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey,
funcid = 'c'; // override the param
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \
if(!vopretNonfungible.empty()) ss << vopretNonfungible );
if (!vopretNonfungible.empty()) { \
ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; \
ss << vopretNonfungible; \
});
return(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)
{
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 EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload)
{
CScript opret;
uint8_t tokenFuncId = 't';
@@ -73,7 +70,7 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std:
if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2)
ccType = voutPubkeys.size();
else {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl);
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl);
}
std::vector<uint8_t> vpayload;
@@ -82,10 +79,11 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std:
opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \
if (ccType >= 1) ss << voutPubkeys[0]; \
if (ccType == 2) ss << voutPubkeys[1]; \
if (vpayloadNonfungible.size() > 0) ss << vpayloadNonfungible; \
if (vpayload.size() > 0) ss << vpayload);
// "error 64: scriptpubkey":
// bad opret cases (retries to attach payload without re-serialization):
// "error 64: scriptpubkey":
// if (payload.size() > 0)
// opret += payload;
@@ -93,8 +91,7 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std:
// CScript opretPayloadNoOpcode(vpayload);
// return opret + opretPayloadNoOpcode;
// how to attach payload without re-serialization:
// sig_aborted:
// error sig_aborted:
// opret.resize(opret.size() + vpayload.size());
// CScript::iterator it = opret.begin() + opret.size();
// for (int i = 0; i < vpayload.size(); i++, it++)
@@ -117,16 +114,20 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t>
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, fieldId = 0;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
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; \
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 ) )
if (!ss.eof()) { \
ss >> fieldId; \
if( fieldId == OPRETID_NONFUNGIBLEDATA ) \
ss >> vopretNonfungible; \
}))
return(funcid);
}
return (uint8_t)0;
@@ -134,7 +135,7 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t>
// 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) {
/*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);
@@ -150,18 +151,22 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256
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)
// decodes token opret:
// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets').
// for 'c' returns only funcid. NOTE: nonfungible data is not returned
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra)
{
std::vector<uint8_t> vopret, extra, dummyPubkey;
uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType;
std::vector<uint8_t> vopret, extra, dummyPubkey, vnonfungibleDummy;
uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType, fieldId = 0;
std::string dummyName; std::string dummyDescription;
CPubKey voutPubkey1, voutPubkey2;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
tokenid = zeroid;
vopretExtra.clear();
if (script != NULL && vopret.size() > 2)
{
@@ -173,12 +178,12 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
return (uint8_t)0;
funcId = script[1];
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' '));
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' '));
switch( funcId )
{
case 'c':
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vopret1);
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vnonfungibleDummy);
//break;
case 't':
//not used yet: case 'l':
@@ -187,9 +192,7 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
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 (!isEof) ss >> vopretExtra; \
// if something else remains -> bad format
isEof = ss.eof()) || !isEof)
{
@@ -230,7 +233,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore;
std::vector<uint8_t> vopret1, vopret2, tmporigpubkey, ignorepubkey;
std::vector<uint8_t> vopretExtra, tmporigpubkey, ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], CCaddr[64];
std::vector<CPubKey> voutTokenPubkeys;
@@ -242,7 +245,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopret1, vopret2)) == 0)
// check boundaries:
if (numvouts < 1)
return eval->Invalid("no vouts");
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0)
return eval->Invalid("TokenValidate: invalid opreturn payload");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << cp->evalcode << std::endl);
@@ -250,13 +257,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
return eval->Invalid("cant find token create txid");
//else if (IsCCInput(tx.vin[0].scriptSig) != 0)
// return eval->Invalid("illegal token vin0");
else if (numvouts < 1)
return eval->Invalid("no vouts");
// return eval->Invalid("illegal token vin0"); // this validation was removed because some token tx might not have normal vins
else if (funcid != 'c')
{
if (tokenid == zeroid)
return eval->Invalid("illegal tokenid");
if (tokenid == zeroid)
return eval->Invalid("illegal tokenid");
else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) {
if (!eval->Valid())
return false; //TokenExactAmounts must call eval->Invalid()!
@@ -265,14 +270,7 @@ 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)
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
@@ -413,12 +411,26 @@ uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) {
return (uint8_t)0;
}
// remove token->unspendablePk (it is only for marker usage)
std::vector<CPubKey> FilterOutTokensUnspendablePk(std::vector<CPubKey> sourcePubkeys) {
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
CPubKey tokensUnspendablePk = GetUnspendable(cpTokens, NULL);
std::vector<CPubKey> destPubkeys;
for (auto pk : sourcePubkeys)
if (pk != tokensUnspendablePk)
destPubkeys.push_back(pk);
return destPubkeys;
}
// Checks if the vout is a really Tokens CC vout
// also checks tokenid in opret or txid if this is 'c' tx
// goDeeper is true: the func also validates amounts of the passed transaction:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true!
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 /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid)
{
// this is just for log messages indentation fur debugging recursive calls:
@@ -458,36 +470,43 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
// token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'):
const uint8_t funcId = ValidateTokenOpret(tx, reftokenid);
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (funcId != 0) {
LOGSTREAM((char *)"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 (funcId != 0) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
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::vector<CPubKey> voutPubkeys, voutPubkeysInOpret;
std::vector<uint8_t> vopretExtra, vopretNonfungible;
if (checkPubkeys && funcId != 'c') { // for 'c' there is no pubkeys
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
// test vouts for possible token use-cases:
std::vector<std::pair<CTxOut, std::string>> testVouts;
DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, vopretExtra);
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl);
GetNonfungibleData(reftokenid, vopretNonfungible);
voutPubkeys = FilterOutTokensUnspendablePk(voutPubkeysInOpret);
// NOTE: evalcode order in vouts is important:
// non-fungible-eval -> EVAL_TOKENS -> assets-eval
if (vopretNonfungible.size() > 0)
evalCode = vopretNonfungible.begin()[0];
if (vopretExtra.size() > 0)
evalCode2 = vopretExtra.begin()[0];
if (evalCode == EVAL_TOKENS && evalCode2 != 0) {
evalCode = evalCode2;
evalCode2 = 0;
}
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:
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret1=" << HexStr(vopret1) << std::endl);
LOGSTREAM((char *)"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?
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) {
// check dual/three-eval 1 pubkey vout with the first pubkey
@@ -530,8 +549,10 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
}
// maybe it is single-eval or dual/three-eval token change?
std::vector<CPubKey> vinPubkeys;
ExtractTokensVinPubkeys(tx, vinPubkeys);
std::vector<CPubKey> vinPubkeys, vinPubkeysUnfiltered;
ExtractTokensVinPubkeys(tx, vinPubkeysUnfiltered);
vinPubkeys = FilterOutTokensUnspendablePk(vinPubkeysUnfiltered);
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) {
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk")));
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
@@ -541,19 +562,36 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
}
// try all test vouts:
for (auto t : testVouts) {
if (t.first == tx.vout[v]) {
LOGSTREAM((char *)"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((char *)"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 {
LOGSTREAM((char *)"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;
CPubKey origPubkey;
std::vector<uint8_t> vorigPubkey;
std::string dummyName, dummyDescription;
std::vector<uint8_t> vopret1;
if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, vopret1) == 0) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
return 0;
}
origPubkey = pubkey2pk(vorigPubkey);
// for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation)
// maybe this is like gatewayclaim to single-eval token?
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk")));
// maybe this is like FillSell for non-fungible token?
if (evalCode != 0)
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk")));
}
// try all test vouts:
for (auto t : testVouts) {
if (t.first == tx.vout[v]) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, 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((char *)"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);
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
@@ -637,6 +675,7 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu
return true;
}
// get non-fungible data from 'tokenbase' tx (the data might be empty)
void GetNonfungibleData(uint256 tokenid, std::vector<uint8_t> &vopretNonfungible)
{
@@ -644,18 +683,18 @@ void GetNonfungibleData(uint256 tokenid, std::vector<uint8_t> &vopretNonfungible
uint256 hashBlock;
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "SetNonfungibleEvalCode() cound not load token creation tx=" << tokenid.GetHex() << std::endl);
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() 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> origpubkey;
std::string name, description;
std::vector<uint8_t> vopretExtra;
DecodeTokenOpRet(tokenbasetx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretNonfungible, vopretExtra);
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, vopretExtra) == 'c')
vopretNonfungible = vopretExtra;
}
}
@@ -786,7 +825,9 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st
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)); // old marker (non-burnable because spending could not be validated)
// NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout():
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData)));
}
@@ -838,7 +879,7 @@ std::string TokenTransfer(int64_t txfee, uint256 tokenid, std::vector<uint8_t> d
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, vopretNonfungible, CScript())));
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, CScript())));
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "not enough CC token inputs for amount=" << total << std::endl);
@@ -913,22 +954,31 @@ UniValue TokenList()
{
UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressIndexCCMarker;
struct CCcontract_info *cp, C; uint256 txid, hashBlock;
CTransaction vintx; std::vector<uint8_t> origpubkey;
std::string name, description; char str[65];
std::string name, description;
cp = CCinit(&C, EVAL_TOKENS);
SetCCtxids(addressIndex, cp->normaladdr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++)
{
txid = it->first.txhash;
if (GetTransaction(txid, vintx, hashBlock, false) != 0)
{
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0)
{
result.push_back(uint256_str(str, txid));
}
}
auto addTokenId = [&](uint256 txid) {
if (GetTransaction(txid, vintx, hashBlock, false) != 0) {
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) {
result.push_back(txid.GetHex());
}
}
};
SetCCtxids(addressIndex, cp->normaladdr); // find by old normal addr marker
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) {
addTokenId(it->first.txhash);
}
SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr); // find by burnable validated cc addr marker
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) {
addTokenId(it->first.txhash);
}
return(result);
}

View File

@@ -35,11 +35,4 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid);
UniValue TokenInfo(uint256 tokenid);
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 uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
#endif

View File

@@ -157,7 +157,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti
if (numvouts == 0)
return eval->Invalid("AssetValidate: no vouts");
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey, vopretNonfungibleDummy)) == 0 )
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
return eval->Invalid("AssetValidate: invalid opreturn payload");
// non-fungible tokens support:

View File

@@ -15,6 +15,7 @@
******************************************************************************/
#include "cJSON.h"
#include "CCinclude.h"
#define ROGUE_REGISTRATION 5
#define ROGUE_REGISTRATIONSIZE (100 * 10000)
@@ -173,19 +174,27 @@ CScript rogue_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CP
return(opret);
}
uint8_t rogue_highlanderopretdecode(uint256 &gametxid,int32_t &regslot,CPubKey &pk,std::vector<uint8_t> &playerdata,CScript scriptPubKey)
uint8_t rogue_highlanderopretdecode(uint256 &gametxid, int32_t &regslot, CPubKey &pk, std::vector<uint8_t> &playerdata, CScript scriptPubKey)
{
std::vector<uint8_t> vopret,vopret2; uint8_t e,f; uint256 tokenid; std::vector<CPubKey> voutPubkeys;
GetOpReturnData(scriptPubKey,vopret);
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') )
std::string name, description;
std::vector<uint8_t> vorigPubkey;
std::vector<uint8_t> vopretNonfungible, vopret, vopretDummy;
uint8_t e, f; uint256 tokenid; std::vector<CPubKey> voutPubkeys;
GetOpReturnData(scriptPubKey, vopret);
// try no tokens case:
if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q'))
return(f);
else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret,vopret2)) == 'c' || f == 't' )
{
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') )
else if (f = DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, vopretDummy) != 0) { // it is tokens
if (f != 'c')
GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx
if (vopretNonfungible.size() > 2 && E_UNMARSHAL(vopretNonfungible, ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q'))
{
return(f);
}
}
return(0);
}
@@ -209,7 +218,7 @@ uint8_t rogue_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &pl
{
return(f);
}
else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret,vopret2)) == 'c' || f == 't' )
else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret)) == 'c' || f == 't' )
{
//fprintf(stderr,"got valid token opret %s e.%d vs %d\n",tokenid.GetHex().c_str(),e,EVAL_TOKENS);
if ( vopret2.size() > 2 && E_UNMARSHAL(vopret2,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_ROGUE && f == 'R' )
@@ -670,6 +679,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
// vout0 -> keystrokes/completion baton
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,roguepk,burnpk; CTransaction tx,playertx; std::vector<uint8_t> playerdata; std::string rawtx; bits256 t;
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
@@ -707,7 +717,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
CCaddr1of2set(cp,roguepk,roguepk,cp->CCpriv,destaddr);
mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk));
std::vector<uint8_t> vopretFinish, vopret2; uint8_t e, funcid; uint256 tokenid; std::vector<CPubKey> voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0;
std::vector<uint8_t> vopretExtra; uint8_t e, funcid; uint256 tokenid; std::vector<CPubKey> voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0;
CScript opretRegister = rogue_registeropret(gametxid, playertxid);
if ( playertxid != zeroid )
{
@@ -715,13 +725,13 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
voutPubkeysEmpty.push_back(burnpk);
if ( GetTransaction(playertxid,playertx,hashBlock,false) != 0 )
{
if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tokenid, voutPubkeys, vopretFinish, vopret2)) != 0)
if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tokenid, voutPubkeys, vopretExtra)) != 0)
{ // if token in the opret
didtx = 1;
if ( funcid == 'c' )
tokenid = playertxid;
rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee,
EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, vopretFinish /*=non-fungible*/, opretRegister));
EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, opretRegister));
}
}
}
@@ -788,6 +798,8 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param
// get any playerdata, get all keystrokes, replay game and compare final state
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
UniValue result(UniValue::VOBJ); std::string rawtx; CTransaction gametx; uint64_t seed,mult; int64_t buyin,batonvalue,inputsum,cashout,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char myrogueaddr[64],*keystrokes = 0; std::vector<uint8_t> playerdata,newdata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,roguepk; uint8_t player[10000],mypriv[32],funcid;
struct CCcontract_info *cpTokens, tokensC;
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
@@ -869,7 +881,10 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param
}
}
}
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-2*txfee),roguepk));
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),roguepk));
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // marker to token cc addr, burnable and validated
Myprivkey(mypriv);
CCaddr1of2set(cp,roguepk,mypk,mypriv,myrogueaddr);
CScript opret = rogue_highlanderopret(funcid, gametxid, regslot,mypk, newdata);