Merge pull request #1442 from dimxy/token-migration
Token crosschain migration support in fungible chain cluster
This commit is contained in:
@@ -206,13 +206,13 @@ int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex);
|
||||
|
||||
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, vscript_t vopretNonfungible);
|
||||
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId);
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk);
|
||||
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<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
|
||||
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible);
|
||||
bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vinPubkeys);
|
||||
@@ -293,6 +293,8 @@ void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uin
|
||||
bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen);
|
||||
UniValue ValueFromAmount(const CAmount& amount);
|
||||
|
||||
int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey);
|
||||
int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey);
|
||||
|
||||
// bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream:
|
||||
#define CCLOG_INFO 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2018 The SuperNET Developers. *
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
@@ -14,6 +14,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCtokens.h"
|
||||
#include "importcoin.h"
|
||||
|
||||
/* TODO: correct this:
|
||||
-----------------------------
|
||||
@@ -102,22 +103,21 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
|
||||
|
||||
switch (funcid)
|
||||
{
|
||||
case 'c': // create wont be called to be verified as it has no CC inputs
|
||||
case 'c': // token create should not be validated as it has no CC inputs, so return 'invalid'
|
||||
// token tx structure for 'c':
|
||||
//vin.0: normal input
|
||||
//vout.0: issuance tokenoshis to CC
|
||||
//vout.1: normal output for change (if any)
|
||||
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
|
||||
//if (evalCodeInOpret != EVAL_TOKENS)
|
||||
// return eval->Invalid("unexpected TokenValidate for createtoken");
|
||||
//else
|
||||
return true;
|
||||
return eval->Invalid("incorrect token funcid");
|
||||
|
||||
case 't': // transfer
|
||||
// token tx structure for 't'
|
||||
//vin.0: normal input
|
||||
//vin.1 .. vin.n-1: valid CC outputs
|
||||
//vout.0 to n-2: tokenoshis output to CC
|
||||
//vout.n-2: normal output for change (if any)
|
||||
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
|
||||
//vout.n-1: opreturn EVAL_TOKENS 't' tokenid <other contract payload>
|
||||
if (inputs == 0)
|
||||
return eval->Invalid("no token inputs for transfer");
|
||||
|
||||
@@ -129,20 +129,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
|
||||
return eval->Invalid("unexpected token funcid");
|
||||
}
|
||||
|
||||
// forward validation if evalcode in opret is not EVAL_TOKENS
|
||||
// init for forwarding validation call
|
||||
//if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS?
|
||||
// struct CCcontract_info *cpOther = NULL, C;
|
||||
|
||||
// cpOther = CCinit(&C, evalCodeInOpret);
|
||||
// if (cpOther)
|
||||
// return cpOther->validate(cpOther, eval, tx, nIn);
|
||||
// else
|
||||
// return eval->Invalid("unsupported evalcode in opret");
|
||||
//}
|
||||
return true;
|
||||
// what does this do?
|
||||
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
|
||||
}
|
||||
|
||||
// helper funcs:
|
||||
@@ -333,7 +320,8 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
vscript_t vopretExtra, vopretNonfungible;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
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 evalCodeNonfungible = 0;
|
||||
uint8_t evalCode1 = 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:
|
||||
@@ -354,12 +342,12 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
// non-fungible-eval -> EVAL_TOKENS -> assets-eval
|
||||
|
||||
if (vopretNonfungible.size() > 0)
|
||||
evalCode = vopretNonfungible.begin()[0];
|
||||
evalCodeNonfungible = evalCode1 = vopretNonfungible.begin()[0];
|
||||
if (vopretExtra.size() > 0)
|
||||
evalCode2 = vopretExtra.begin()[0];
|
||||
|
||||
if (evalCode == EVAL_TOKENS && evalCode2 != 0) {
|
||||
evalCode = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...)
|
||||
if (evalCode1 == EVAL_TOKENS && evalCode2 != 0) {
|
||||
evalCode1 = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...)
|
||||
evalCode2 = 0;
|
||||
}
|
||||
|
||||
@@ -369,39 +357,41 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
// 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
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode1, 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")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) );
|
||||
|
||||
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")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, 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]")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, 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")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode1, 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")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval")));
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this is like gatewayclaim to single-eval token?
|
||||
if( evalCodeNonfungible == 0 ) // do not allow to convert non-fungible to fungible 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 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( evalCode1 != 0 )
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, 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]")));
|
||||
|
||||
// the same for pk[1]:
|
||||
if (voutPubkeys.size() == 2) {
|
||||
// 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 (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]")));
|
||||
if (evalCode1 != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, 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]")));
|
||||
}
|
||||
@@ -413,52 +403,95 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there)
|
||||
|
||||
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")));
|
||||
if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
|
||||
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(evalCode1, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
|
||||
|
||||
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")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
CPubKey origPubkey;
|
||||
vscript_t vorigPubkey;
|
||||
std::string dummyName, dummyDescription;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 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;
|
||||
// try all test vouts:
|
||||
for (auto t : testVouts) {
|
||||
if (t.first == tx.vout[v]) { // test vout matches
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
return tx.vout[v].nValue;
|
||||
}
|
||||
}
|
||||
|
||||
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")));
|
||||
}
|
||||
else { // funcid == 'c'
|
||||
|
||||
if (!tx.IsCoinImport()) {
|
||||
|
||||
// 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;
|
||||
vscript_t vorigPubkey;
|
||||
std::string dummyName, dummyDescription;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 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;
|
||||
}
|
||||
|
||||
CPubKey origPubkey = pubkey2pk(vorigPubkey);
|
||||
|
||||
|
||||
// TODO: add voutPubkeys for 'c' tx
|
||||
|
||||
/* this would not work for imported tokens:
|
||||
// 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?
|
||||
if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible 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 (evalCode1 != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); */
|
||||
|
||||
// note: this would not work if there are several pubkeys in the tokencreator's wallet (AddNormalinputs does not use pubkey param):
|
||||
// for tokenbase tx check that normal inputs sent from origpubkey > cc outputs
|
||||
int64_t ccOutputs = 0;
|
||||
for (auto vout : tx.vout)
|
||||
if (vout.scriptPubKey.IsPayToCryptoCondition() //TODO: add voutPubkey validation
|
||||
&& !IsTokenMarkerVout(vout)) // should not be marker here
|
||||
ccOutputs += vout.nValue;
|
||||
|
||||
int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // check if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey)
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl);
|
||||
|
||||
if (normalInputs >= ccOutputs) {
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl);
|
||||
if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
|
||||
return tx.vout[v].nValue;
|
||||
else
|
||||
return 0; // vout is good, but do not take marker into account
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// imported tokens are checked in the eval::ImportCoin() validation code
|
||||
if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
|
||||
return tx.vout[v].nValue;
|
||||
else
|
||||
return 0; // vout is good, but do not take marker into account
|
||||
}
|
||||
}
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " 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());
|
||||
}
|
||||
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool IsTokenMarkerVout(CTxOut vout) {
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
return vout == MakeCC1vout(EVAL_TOKENS, vout.nValue, GetUnspendable(cpTokens, NULL));
|
||||
}
|
||||
|
||||
// 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 reftokenid)
|
||||
{
|
||||
@@ -582,6 +615,7 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C
|
||||
GetTokensCCaddress(cp, tokenaddr, pk);
|
||||
SetCCunspents(unspentOutputs, tokenaddr,true);
|
||||
|
||||
|
||||
if (unspentOutputs.empty()) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl);
|
||||
}
|
||||
@@ -747,7 +781,7 @@ CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) {
|
||||
return CPubKey(); //return invalid pubkey
|
||||
}
|
||||
|
||||
|
||||
// returns token creation signed raw tx
|
||||
std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
@@ -767,7 +801,7 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st
|
||||
cp = CCinit(&C, EVAL_TOKENS);
|
||||
if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level
|
||||
{
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl);
|
||||
CCerror = "name should be <= 32, description should be <= 4096";
|
||||
return("");
|
||||
}
|
||||
@@ -777,6 +811,13 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, tokensupply + 2 * txfee, 64) > 0)
|
||||
{
|
||||
|
||||
int64_t mypkInputs = TotalPubkeyNormalInputs(mtx, mypk);
|
||||
if (mypkInputs < tokensupply) { // check that tokens amount are really issued with mypk (because in the wallet there maybe other privkeys)
|
||||
CCerror = "some inputs signed not with -pubkey=pk";
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
uint8_t destEvalCode = EVAL_TOKENS;
|
||||
if( nonfungibleData.size() > 0 )
|
||||
destEvalCode = nonfungibleData.begin()[0];
|
||||
@@ -843,7 +884,7 @@ std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey,
|
||||
}
|
||||
else {
|
||||
CCerror = strprintf("no token inputs");
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << total << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << " for amount=" << total << std::endl);
|
||||
}
|
||||
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
|
||||
}
|
||||
@@ -880,7 +921,7 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
uint256 hashBlock;
|
||||
CTransaction vintx;
|
||||
CTransaction tokenbaseTx;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
vscript_t vopretNonfungible;
|
||||
@@ -889,14 +930,14 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
|
||||
cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS);
|
||||
|
||||
if( !GetTransaction(tokenid, vintx, hashBlock, false) )
|
||||
if( !GetTransaction(tokenid, tokenbaseTx, hashBlock, false) )
|
||||
{
|
||||
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, oprets) != 'c')
|
||||
if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c')
|
||||
{
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl);
|
||||
result.push_back(Pair("result", "error"));
|
||||
@@ -909,8 +950,8 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
result.push_back(Pair("name", name));
|
||||
|
||||
int64_t supply = 0, output;
|
||||
for (int v = 0; v < vintx.vout.size() - 1; v++)
|
||||
if ((output = IsTokensvout(false, true, cpTokens, NULL, vintx, v, tokenid)) > 0)
|
||||
for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++)
|
||||
if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0)
|
||||
supply += output;
|
||||
result.push_back(Pair("supply", supply));
|
||||
result.push_back(Pair("description", description));
|
||||
@@ -919,6 +960,40 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
if( !vopretNonfungible.empty() )
|
||||
result.push_back(Pair("data", HexStr(vopretNonfungible)));
|
||||
|
||||
if (tokenbaseTx.IsCoinImport()) { // if imported token
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
CTxDestination importaddress;
|
||||
|
||||
std::string sourceSymbol = "can't decode";
|
||||
std::string sourceTokenId = "can't decode";
|
||||
|
||||
if (UnmarshalImportTx(tokenbaseTx, proof, burnTx, payouts))
|
||||
{
|
||||
// extract op_return to get burn source chain.
|
||||
std::vector<uint8_t> burnOpret;
|
||||
std::string targetSymbol;
|
||||
uint32_t targetCCid;
|
||||
uint256 payoutsHash;
|
||||
std::vector<uint8_t> rawproof;
|
||||
if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) {
|
||||
if (rawproof.size() > 0) {
|
||||
CTransaction tokenbasetx;
|
||||
E_UNMARSHAL(rawproof, ss >> sourceSymbol;
|
||||
if (!ss.eof())
|
||||
ss >> tokenbasetx);
|
||||
|
||||
if (!tokenbasetx.IsNull())
|
||||
sourceTokenId = tokenbasetx.GetHash().GetHex();
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_back(Pair("IsImported", "yes"));
|
||||
result.push_back(Pair("sourceChain", sourceSymbol));
|
||||
result.push_back(Pair("sourceTokenId", sourceTokenId));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, st
|
||||
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total);
|
||||
int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid);
|
||||
CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey);
|
||||
bool IsTokenMarkerVout(CTxOut vout);
|
||||
|
||||
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid);
|
||||
UniValue TokenInfo(uint256 tokenid);
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
// encode decode tokens opret
|
||||
// (moved to a separate file to enable linking lib common.so with importcoin.cpp)
|
||||
// make token cryptoconditions and vouts
|
||||
// This code was moved to a separate source file to enable linking libcommon.so (with importcoin.cpp which depends on some token functions)
|
||||
|
||||
#include "CCtokens.h"
|
||||
|
||||
@@ -44,6 +60,7 @@ CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey,
|
||||
return(opret);
|
||||
}
|
||||
|
||||
/*
|
||||
// opret 'i' for imported tokens
|
||||
CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> oprets)
|
||||
{
|
||||
@@ -62,7 +79,7 @@ CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name
|
||||
});
|
||||
return(opret);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId)
|
||||
@@ -158,37 +175,9 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t>
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
// for imported tokens
|
||||
uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &oprets)
|
||||
{
|
||||
vscript_t vopret, vblob;
|
||||
uint8_t dummyEvalcode, funcid, opretId = 0;
|
||||
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
oprets.clear();
|
||||
|
||||
if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'i')
|
||||
{
|
||||
if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; ss >> srctokenid;
|
||||
while (!ss.eof()) {
|
||||
ss >> opretId;
|
||||
if (!ss.eof()) {
|
||||
ss >> vblob;
|
||||
oprets.push_back(std::make_pair(opretId, vblob));
|
||||
}
|
||||
}))
|
||||
{
|
||||
srctokenid = revuint256(srctokenid); // do not forget this
|
||||
return(funcid);
|
||||
}
|
||||
}
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenImportOpRet() incorrect token import opret" << std::endl);
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
// decodes token opret:
|
||||
// decode token opret:
|
||||
// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets').
|
||||
// for 'c' and 'i' returns only funcid. NOTE: nonfungible data is not returned
|
||||
// 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<std::pair<uint8_t, vscript_t>> &oprets)
|
||||
{
|
||||
vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy;
|
||||
@@ -207,9 +196,6 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
|
||||
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;
|
||||
|
||||
evalCodeTokens = script[0];
|
||||
if (evalCodeTokens != EVAL_TOKENS) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect evalcode in tokens opret" << std::endl);
|
||||
@@ -217,15 +203,13 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
}
|
||||
|
||||
funcId = script[1];
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet() decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl);
|
||||
|
||||
switch (funcId)
|
||||
{
|
||||
case 'c':
|
||||
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets);
|
||||
case 'i':
|
||||
return DecodeTokenImportOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, dummySrcTokenId, oprets);
|
||||
//break;
|
||||
|
||||
case 't':
|
||||
|
||||
// compatibility with old-style rogue or assets data (with no opretid):
|
||||
@@ -293,4 +277,75 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
}
|
||||
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
// make 1of2 sigs cond
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk1));
|
||||
pks.push_back(CCNewSecp256k1(pk2));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // signature
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
|
||||
return MakeTokensCCcond1(evalcode, 0, pk);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
|
||||
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
|
||||
}
|
||||
|
||||
@@ -93,79 +93,6 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, c
|
||||
return(vout);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
// make 1of2 sigs cond
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk1));
|
||||
pks.push_back(CCNewSecp256k1(pk2));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) );
|
||||
if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if( evalcode2 != 0 )
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // signature
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
|
||||
return MakeTokensCCcond1(evalcode, 0, pk);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
|
||||
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
|
||||
}
|
||||
|
||||
|
||||
CC* GetCryptoCondition(CScript const& scriptSig)
|
||||
{
|
||||
auto pc = scriptSig.begin();
|
||||
@@ -709,6 +636,57 @@ CPubKey check_signing_pubkey(CScript scriptSig)
|
||||
return CPubKey();
|
||||
}
|
||||
|
||||
|
||||
// returns total of normal inputs signed with this pubkey
|
||||
int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey)
|
||||
{
|
||||
int64_t total = 0;
|
||||
for (auto vin : tx.vin) {
|
||||
CTransaction vintx;
|
||||
uint256 hashBlock;
|
||||
if (!IsCCInput(vin.scriptSig) && myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
std::vector<valtype> vSolutions;
|
||||
txnouttype whichType;
|
||||
|
||||
if (Solver(vintx.vout[vin.prevout.n].scriptPubKey, whichType, vSolutions)) {
|
||||
switch (whichType) {
|
||||
case TX_PUBKEY:
|
||||
if (pubkey == CPubKey(vSolutions[0])) // is my input?
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
break;
|
||||
case TX_PUBKEYHASH:
|
||||
if (pubkey.GetID() == CKeyID(uint160(vSolutions[0]))) // is my input?
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// returns total of CC inputs signed with this pubkey
|
||||
int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey)
|
||||
{
|
||||
int64_t total = 0;
|
||||
for (auto vin : tx.vin) {
|
||||
if (IsCCInput(vin.scriptSig)) {
|
||||
CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig);
|
||||
if (vinPubkey.IsValid()) {
|
||||
if (vinPubkey == pubkey) {
|
||||
CTransaction vintx;
|
||||
uint256 hashBlock;
|
||||
if (myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
|
||||
{
|
||||
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector<uint8_t> origpubkey;
|
||||
|
||||
@@ -20,9 +20,18 @@
|
||||
#include "primitives/transaction.h"
|
||||
#include "cc/CCinclude.h"
|
||||
#include <openssl/sha.h>
|
||||
#include "cc/CCtokens.h"
|
||||
|
||||
#include "key_io.h"
|
||||
#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA"
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||
* to protect it from malleability.
|
||||
|
||||
##### 0xffffffff is a special CCid for single chain/dual daemon imports
|
||||
*/
|
||||
|
||||
extern std::string ASSETCHAINS_SELFIMPORT;
|
||||
extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT;
|
||||
@@ -61,64 +70,65 @@ cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,
|
||||
}
|
||||
|
||||
// makes source tx for self import tx
|
||||
std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx)
|
||||
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount)
|
||||
{
|
||||
const int64_t txfee = 10000;
|
||||
int64_t inputs, change;
|
||||
CPubKey myPubKey = Mypubkey();
|
||||
struct CCcontract_info *cpDummy, C;
|
||||
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS);
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS); // this is just for FinalizeCCTx to work
|
||||
|
||||
mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
|
||||
if( (inputs = AddNormalinputs(mtx, myPubKey, txfee, 4)) == 0 ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx: cannot find normal imputs for txfee" << std::endl);
|
||||
return std::string("");
|
||||
if (AddNormalinputs(mtx, myPubKey, 2 * txfee, 4) == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl);
|
||||
}
|
||||
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
mtx.vout.push_back(CTxOut(txfee, scriptPubKey));
|
||||
change = inputs - txfee;
|
||||
if( change != 0 )
|
||||
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG));
|
||||
|
||||
//make opret with amount:
|
||||
return FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
|
||||
//make opret with 'burned' amount:
|
||||
FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
// make sure vin0 is signed by ASSETCHAINS_OVERRIDE_PUBKEY33
|
||||
int32_t CheckVin0PubKey(const CTransaction &sourcetx)
|
||||
// make sure vin is signed by pubkey33
|
||||
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33])
|
||||
{
|
||||
CTransaction vintx;
|
||||
uint256 blockHash;
|
||||
char destaddr[64], pkaddr[64];
|
||||
|
||||
if( !myGetTransaction(sourcetx.vin[0].prevout.hash, vintx, blockHash) ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() could not load vintx" << sourcetx.vin[0].prevout.hash.GetHex() << std::endl);
|
||||
return(-1);
|
||||
if (i < 0 || i >= sourcetx.vin.size())
|
||||
return false;
|
||||
|
||||
if( !myGetTransaction(sourcetx.vin[i].prevout.hash, vintx, blockHash) ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl);
|
||||
return false;
|
||||
}
|
||||
if( sourcetx.vin[0].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[0].prevout.n].scriptPubKey) != 0 )
|
||||
if( sourcetx.vin[i].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[i].prevout.n].scriptPubKey) != 0 )
|
||||
{
|
||||
pubkey2addr(pkaddr, ASSETCHAINS_OVERRIDE_PUBKEY33);
|
||||
pubkey2addr(pkaddr, pubkey33);
|
||||
if (strcmp(pkaddr, destaddr) == 0) {
|
||||
return(0);
|
||||
return true;
|
||||
}
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() mismatched vin0[prevout.n=" << sourcetx.vin[0].prevout.n << "] -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
|
||||
}
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ac_import=PUBKEY support:
|
||||
// prepare a tx for creating import tx and quasi-burn tx
|
||||
int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount) // find burnTx with hash from "other" daemon
|
||||
int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull) // find burnTx with hash from "other" daemon
|
||||
{
|
||||
MerkleBranch newBranch;
|
||||
CMutableTransaction tmpmtx;
|
||||
CTransaction sourcetx;
|
||||
//CTransaction sourcetx;
|
||||
|
||||
tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
|
||||
/*
|
||||
if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl);
|
||||
return(-1);
|
||||
@@ -127,9 +137,9 @@ int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript
|
||||
if (sourcetx.vout.size() == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
} */
|
||||
|
||||
if (ivout < 0) { // "ivout < 0" means "find"
|
||||
/*if (ivout < 0) { // "ivout < 0" means "find"
|
||||
// try to find vout
|
||||
CPubKey myPubkey = Mypubkey();
|
||||
ivout = 0;
|
||||
@@ -141,38 +151,49 @@ int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript
|
||||
if (ivout >= sourcetx.vout.size()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
} */
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
|
||||
int32_t ivout = 0;
|
||||
|
||||
scriptPubKey = sourcetx.vout[ivout].scriptPubKey;
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
|
||||
|
||||
CScript scriptPubKey = sourceMtx.vout[ivout].scriptPubKey;
|
||||
|
||||
//mtx is template for import tx
|
||||
mtx = sourcetx;
|
||||
mtx.fOverwintered = tmpmtx.fOverwintered;
|
||||
templateMtx = sourceMtx;
|
||||
templateMtx.fOverwintered = tmpmtx.fOverwintered;
|
||||
|
||||
//malleability fix for burn tx:
|
||||
//mtx.nExpiryHeight = tmpmtx.nExpiryHeight;
|
||||
mtx.nExpiryHeight = sourcetx.nExpiryHeight;
|
||||
templateMtx.nExpiryHeight = sourceMtx.nExpiryHeight;
|
||||
|
||||
mtx.nVersionGroupId = tmpmtx.nVersionGroupId;
|
||||
mtx.nVersion = tmpmtx.nVersion;
|
||||
mtx.vout.clear();
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].nValue = burnAmount;
|
||||
mtx.vout[0].scriptPubKey = scriptPubKey;
|
||||
templateMtx.nVersionGroupId = tmpmtx.nVersionGroupId;
|
||||
templateMtx.nVersion = tmpmtx.nVersion;
|
||||
templateMtx.vout.clear();
|
||||
templateMtx.vout.resize(1);
|
||||
|
||||
// not sure we need this now as we create sourcetx ourselves:
|
||||
if (sourcetx.GetHash() != sourcetxid) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
// check ac_pubkey:
|
||||
if (CheckVin0PubKey(sourcetx) < 0) {
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t burnAmount;
|
||||
vscript_t vopret;
|
||||
if( !GetOpReturnData(sourceMtx.vout.back().scriptPubKey, vopret) ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> burnAmount)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
proof = std::make_pair(sourcetxid, newBranch);
|
||||
templateMtx.vout[0].nValue = burnAmount;
|
||||
templateMtx.vout[0].scriptPubKey = scriptPubKey;
|
||||
|
||||
// not sure we need this now as we create sourcetx ourselves:
|
||||
/*if (sourcetx.GetHash() != sourcetxid) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
|
||||
return(-1);
|
||||
}*/
|
||||
|
||||
// check ac_pubkey:
|
||||
if (!CheckVinPubKey(sourceMtx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
proofNull = ImportProof(std::make_pair(sourceMtx.GetHash(), newBranch));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -476,7 +497,7 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
}
|
||||
|
||||
//ac_pubkey check:
|
||||
if (CheckVin0PubKey(sourcetx) < 0) {
|
||||
if (!CheckVinPubKey(sourcetx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -485,9 +506,11 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t amount;
|
||||
|
||||
GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret);
|
||||
if (vopret.size() == 0 || !E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) || evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "no or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
if (!GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret) ||
|
||||
vopret.size() == 0 ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) ||
|
||||
evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -502,21 +525,154 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||
* to protect it from malleability.
|
||||
|
||||
##### 0xffffffff is a special CCid for single chain/dual daemon imports
|
||||
*/
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &importTx,unsigned int nIn)
|
||||
bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction &burnTx, std::vector<CTxOut> & payouts, const ImportProof &proof, const std::vector<uint8_t> &rawproof)
|
||||
{
|
||||
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; int64_t txfee = 10000, amount; int32_t height,burnvout; std::vector<CPubKey> publishers;
|
||||
uint32_t targetCcid; std::string targetSymbol,srcaddr,destaddr,receipt,rawburntx; uint256 payoutsHash,bindtxid,burntxid; std::vector<uint8_t> rawproof;
|
||||
std::vector<uint256> txids; CPubKey destpub;
|
||||
vscript_t vimportOpret;
|
||||
if (!GetOpReturnData(importTx.vout.back().scriptPubKey, vimportOpret) ||
|
||||
vimportOpret.empty())
|
||||
return eval->Invalid("invalid-import-tx-no-opret");
|
||||
|
||||
if ( importTx.vout.size() < 2 )
|
||||
|
||||
uint256 tokenid = zeroid;
|
||||
if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens)
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
vscript_t vnonfungibleOpret;
|
||||
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
|
||||
if (DecodeTokenOpRet(importTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-import-tx-token-opret");
|
||||
|
||||
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init to no non-fungibles
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
|
||||
if (!vnonfungibleOpret.empty())
|
||||
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
|
||||
|
||||
// check if burn tx at least has cc evaltoken vins (we cannot get cc input)
|
||||
bool hasTokenVin = false;
|
||||
for (auto vin : burnTx.vin)
|
||||
if (cpTokens->ismyvin(vin.scriptSig))
|
||||
hasTokenVin = true;
|
||||
if (!hasTokenVin)
|
||||
return eval->Invalid("burn-tx-has-no-token-vins");
|
||||
|
||||
// calc outputs for burn tx
|
||||
CAmount ccBurnOutputs = 0;
|
||||
for (auto v : burnTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
|
||||
ccBurnOutputs += v.nValue;
|
||||
|
||||
// calc outputs for import tx
|
||||
CAmount ccImportOutputs = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
!IsTokenMarkerVout(v)) // should not be marker here
|
||||
ccImportOutputs += v.nValue;
|
||||
|
||||
if (ccBurnOutputs != ccImportOutputs)
|
||||
return eval->Invalid("token-cc-burned-output-not-equal-cc-imported-output");
|
||||
|
||||
}
|
||||
else if (vimportOpret.begin()[0] != EVAL_IMPORTCOIN) {
|
||||
return eval->Invalid("import-tx-incorrect-opret-eval");
|
||||
}
|
||||
|
||||
// for tokens check burn, import, tokenbase tx
|
||||
if (!tokenid.IsNull()) {
|
||||
|
||||
std::string sourceSymbol;
|
||||
CTransaction tokenbaseTx;
|
||||
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbaseTx))
|
||||
return eval->Invalid("cannot-unmarshal-rawproof-for-tokens");
|
||||
|
||||
uint256 sourceTokenId;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
if (burnTx.vout.size() > 0 && DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, sourceTokenId, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-burn-tx-token-opret");
|
||||
|
||||
if (sourceTokenId != tokenbaseTx.GetHash()) // check tokenid in burn tx opret maches the passed tokenbase tx (to prevent cheating by importing user)
|
||||
return eval->Invalid("incorrect-token-creation-tx-passed");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsSrc;
|
||||
vscript_t vorigpubkeySrc;
|
||||
std::string nameSrc, descSrc;
|
||||
if (DecodeTokenCreateOpRet(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0)
|
||||
return eval->Invalid("cannot-decode-token-creation-tx");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsImport;
|
||||
vscript_t vorigpubkeyImport;
|
||||
std::string nameImport, descImport;
|
||||
if (importTx.vout.size() == 0 || DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0)
|
||||
return eval->Invalid("cannot-decode-token-import-tx");
|
||||
|
||||
// check that name,pubkey,description in import tx correspond ones in token creation tx in the source chain:
|
||||
if (vorigpubkeySrc != vorigpubkeyImport ||
|
||||
nameSrc != nameImport ||
|
||||
descSrc != descImport)
|
||||
return eval->Invalid("import-tx-token-params-incorrect");
|
||||
}
|
||||
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
// if (payoutsHash != SerializeHash(payouts)) // done in ImportCoin
|
||||
// return eval->Invalid("wrong-payouts");
|
||||
|
||||
|
||||
TxProof merkleBranchProof;
|
||||
std::vector<uint256> notaryTxids;
|
||||
|
||||
// Check proof confirms existance of burnTx
|
||||
if (proof.IsMerkleBranch(merkleBranchProof)) {
|
||||
uint256 target = merkleBranchProof.second.Exec(burnTx.GetHash());
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Eval::ImportCoin() momom target=" << target.GetHex() << " merkleBranchProof.first=" << merkleBranchProof.first.GetHex() << std::endl);
|
||||
if (!CheckMoMoM(merkleBranchProof.first, target)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl);
|
||||
return eval->Invalid("momom-check-fail");
|
||||
}
|
||||
}
|
||||
else if (proof.IsNotaryTxids(notaryTxids)) {
|
||||
if (!CheckNotariesApproval(burnTx.GetHash(), notaryTxids)) {
|
||||
return eval->Invalid("notaries-approval-check-fail");
|
||||
}
|
||||
}
|
||||
else {
|
||||
return eval->Invalid("invalid-import-proof");
|
||||
}
|
||||
|
||||
/* if (vimportOpret.begin()[0] == EVAL_TOKENS)
|
||||
return eval->Invalid("test-invalid-tokens-are-good!!");
|
||||
else
|
||||
return eval->Invalid("test-invalid-coins-are-good!!"); */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
CAmount txfee = 10000, amount;
|
||||
int32_t height, burnvout;
|
||||
std::vector<CPubKey> publishers;
|
||||
uint32_t targetCcid;
|
||||
std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx;
|
||||
uint256 payoutsHash, bindtxid, burntxid;
|
||||
std::vector<uint8_t> rawproof;
|
||||
std::vector<uint256> txids;
|
||||
CPubKey destpub;
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "Validating import tx..., txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
if (strcmp(ASSETCHAINS_SYMBOL, "CFEKDIMXY6") == 0 && chainActive.Height() <= 44693)
|
||||
return true;
|
||||
|
||||
if (importTx.vout.size() < 2)
|
||||
return Invalid("too-few-vouts");
|
||||
// params
|
||||
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||
@@ -528,38 +684,47 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &impo
|
||||
// burn params
|
||||
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof))
|
||||
return Invalid("invalid-burn-tx");
|
||||
// check burn amount
|
||||
{
|
||||
uint64_t burnAmount = burnTx.vout.back().nValue;
|
||||
if (burnAmount == 0)
|
||||
return Invalid("invalid-burn-amount");
|
||||
uint64_t totalOut = 0;
|
||||
for (int i=0; i<importTx.vout.size(); i++)
|
||||
totalOut += importTx.vout[i].nValue;
|
||||
if (totalOut > burnAmount || totalOut < burnAmount-txfee )
|
||||
return Invalid("payout-too-high-or-too-low");
|
||||
}
|
||||
|
||||
if (burnTx.vout.size() == 0)
|
||||
return Invalid("invalid-burn-tx-no-vouts");
|
||||
|
||||
// check burned normal amount >= import amount && burned amount <= import amount + txfee (extra txfee is for miners and relaying, see GetImportCoinValue() func)
|
||||
CAmount burnAmount = burnTx.vout.back().nValue;
|
||||
if (burnAmount == 0)
|
||||
return Invalid("invalid-burn-amount");
|
||||
CAmount totalOut = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (!v.scriptPubKey.IsPayToCryptoCondition())
|
||||
totalOut += v.nValue;
|
||||
if (totalOut > burnAmount || totalOut < burnAmount - txfee)
|
||||
return Invalid("payout-too-high-or-too-low");
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
if (payoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
if (targetCcid < KOMODO_FIRSTFUNGIBLEID)
|
||||
return Invalid("chain-not-fungible");
|
||||
// Check proof confirms existance of burnTx
|
||||
|
||||
if ( targetCcid != 0xffffffff )
|
||||
{
|
||||
if ( targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol() )
|
||||
|
||||
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
|
||||
return Invalid("importcoin-wrong-chain");
|
||||
uint256 target = proof.second.Exec(burnTx.GetHash());
|
||||
if (!CheckMoMoM(proof.first, target))
|
||||
return Invalid("momom-check-fail");
|
||||
|
||||
if (!CheckMigration(this, importTx, burnTx, payouts, proof, rawproof))
|
||||
return false; // eval->Invalid() is called in the func
|
||||
}
|
||||
else
|
||||
{
|
||||
TxProof merkleBranchProof;
|
||||
if (!proof.IsMerkleBranch(merkleBranchProof))
|
||||
return Invalid("invalid-import-proof-for-0xFFFFFFFF");
|
||||
|
||||
if ( targetSymbol == "BEAM" )
|
||||
{
|
||||
if ( ASSETCHAINS_BEAMPORT == 0 )
|
||||
return Invalid("BEAM-import-without-port");
|
||||
else if ( CheckBEAMimport(proof,rawproof,burnTx,payouts) < 0 )
|
||||
else if ( CheckBEAMimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("BEAM-import-failure");
|
||||
}
|
||||
else if ( targetSymbol == "CODA" )
|
||||
@@ -573,7 +738,7 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &impo
|
||||
{
|
||||
if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" )
|
||||
return Invalid("PUBKEY-import-when-notPUBKEY");
|
||||
else if ( CheckPUBKEYimport(proof,rawproof,burnTx,payouts) < 0 )
|
||||
else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("PUBKEY-import-failure");
|
||||
}
|
||||
else
|
||||
@@ -584,5 +749,9 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &impo
|
||||
return Invalid("GATEWAY-import-failure");
|
||||
}
|
||||
}
|
||||
|
||||
// return Invalid("test-invalid");
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Valid import tx! txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
return Valid();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user