Merge branch 'FSM' into jl777

# Conflicts:
#	src/cc/CCinclude.h
#	src/cc/CCtokens.cpp
#	src/cc/CCtx.cpp
#	src/wallet/rpcwallet.cpp
This commit is contained in:
jl777
2019-02-10 02:24:24 -11:00
13 changed files with 870 additions and 521 deletions

View File

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