Merge pull request #1238 from dimxy/nonfungible-opret-fix
Nonfungible opret fix
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 ®slot,CPubKey &pk,std::vector<uint8_t> &playerdata,CScript scriptPubKey)
|
||||
uint8_t rogue_highlanderopretdecode(uint256 &gametxid, int32_t ®slot, 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);
|
||||
|
||||
Reference in New Issue
Block a user