Browse Source

token migration moved to FSM

jl777
dimxy 5 years ago
parent
commit
4add699cbb
  1. 2
      src/Makefile.am
  2. 6
      src/cc/CCinclude.h
  3. 225
      src/cc/CCtokens.cpp
  4. 1
      src/cc/CCtokens.h
  5. 133
      src/cc/CCtokenutils.cpp
  6. 124
      src/cc/CCutils.cpp
  7. 323
      src/cc/import.cpp
  8. 123
      src/crosschain.cpp
  9. 2
      src/crosschain.h
  10. 205
      src/importcoin.cpp
  11. 79
      src/importcoin.h
  12. 690
      src/rpc/crosschain.cpp
  13. 3
      src/rpc/server.cpp
  14. 3
      src/rpc/server.h

2
src/Makefile.am

@ -491,7 +491,7 @@ libbitcoin_common_a_SOURCES = \
script/sign.cpp \
script/standard.cpp \
transaction_builder.cpp \
cc/CCtokensOpRet.cpp \
cc/CCtokenutils.cpp \
cc/CCutilbits.cpp \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)

6
src/cc/CCinclude.h

@ -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

225
src/cc/CCtokens.cpp

@ -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;
}

1
src/cc/CCtokens.h

@ -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);

133
src/cc/CCtokensOpRet.cpp → src/cc/CCtokenutils.cpp

@ -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);
}

124
src/cc/CCutils.cpp

@ -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;

323
src/cc/import.cpp

@ -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;
@ -60,64 +69,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);
@ -126,9 +136,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;
@ -140,38 +150,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;
}
} */
int32_t ivout = 0;
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
scriptPubKey = sourcetx.vout[ivout].scriptPubKey;
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);
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;
}
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) {
/*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) {
if (!CheckVinPubKey(sourceMtx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
return -1;
}
proof = std::make_pair(sourcetxid, newBranch);
proofNull = ImportProof(std::make_pair(sourceMtx.GetHash(), newBranch));
return 0;
}
@ -470,7 +491,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;
}
@ -479,9 +500,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;
}
@ -496,21 +519,149 @@ 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 CheckFungible(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;
if (strcmp(ASSETCHAINS_SYMBOL, "CFEKDIMXY6") == 0 && chainActive.Height() <= 10699)
return true;
vscript_t vimportOpret;
if (!GetOpReturnData(importTx.vout.back().scriptPubKey, vimportOpret) ||
vimportOpret.empty())
return eval->Invalid("invalid-import-tx-no-opret");
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");
if ( importTx.vout.size() < 2 )
}
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");
}
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;
int32_t height, burnvout;
std::vector<CPubKey> publishers;
uint32_t targetCcid;
std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx;
uint256 payoutsHash, bindtxid;
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 (importTx.vout.size() < 2)
return Invalid("too-few-vouts");
// params
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
@ -522,38 +673,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 (!CheckFungible(this, importTx, burnTx, payouts, proof, rawproof))
return false;
}
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" )
@ -567,7 +727,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
@ -578,5 +738,14 @@ 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);
/*if (vimportOpret.begin()[0] == EVAL_TOKENS)
return Invalid("test-invalid-tokens-are-good!!");
else
return Invalid("test-invalid-coins-are-good!!");*/
return Valid();
}

123
src/crosschain.cpp

@ -18,6 +18,9 @@
#include "importcoin.h"
#include "main.h"
#include "notarisationdb.h"
#include "merkleblock.h"
#include "cc/CCinclude.h"
/*
* The crosschain workflow.
@ -229,22 +232,25 @@ cont:
*/
void CompleteImportTransaction(CTransaction &importTx, int32_t offset)
{
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; std::vector<uint8_t> rawproof;
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; std::vector<uint8_t> rawproof;
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
throw std::runtime_error("Couldn't parse importTx");
throw std::runtime_error("Couldn't unmarshal importTx");
std::string targetSymbol;
uint32_t targetCCid;
uint256 payoutsHash;
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof))
throw std::runtime_error("Couldn't parse burnTx");
throw std::runtime_error("Couldn't unmarshal burnTx");
proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof, offset);
TxProof merkleBranch;
if( !proof.IsMerkleBranch(merkleBranch) )
throw std::runtime_error("Incorrect import tx proof");
TxProof newMerkleBranch = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, merkleBranch, offset);
ImportProof newProof(newMerkleBranch);
importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
importTx = MakeImportCoinTransaction(newProof, burnTx, payouts);
}
bool IsSameAssetChain(const Notarisation &nota) {
return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
};
@ -306,6 +312,111 @@ bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom)
}
/*
* Check notaries approvals for the txoutproofs of burn tx
* (alternate check if MoMoM check has failed)
* Params:
* burntxid - txid of burn tx on the source chain
* rawproof - array of txids of notaries' proofs
*/
bool CheckNotariesApproval(uint256 burntxid, const std::vector<uint256> & notaryTxids)
{
int count = 0;
// get notaries:
uint8_t notaries_pubkeys[64][33];
std::vector< std::vector<uint8_t> > alreadySigned;
//unmarshal notaries approval txids
for(auto notarytxid : notaryTxids ) {
EvalRef eval;
CBlockIndex block;
CTransaction notarytx; // tx with notary approval of txproof existence
// get notary approval tx
if (eval->GetTxConfirmed(notarytxid, notarytx, block)) {
std::vector<uint8_t> vopret;
if (!notarytx.vout.empty() && GetOpReturnData(notarytx.vout.back().scriptPubKey, vopret)) {
std::vector<uint8_t> txoutproof;
if (E_UNMARSHAL(vopret, ss >> txoutproof)) {
CMerkleBlock merkleBlock;
std::vector<uint256> prooftxids;
// extract block's merkle tree
if (E_UNMARSHAL(txoutproof, ss >> merkleBlock)) {
// extract proven txids:
merkleBlock.txn.ExtractMatches(prooftxids);
if (merkleBlock.txn.ExtractMatches(prooftxids) != merkleBlock.header.hashMerkleRoot || // check block merkle root is correct
std::find(prooftxids.begin(), prooftxids.end(), burntxid) != prooftxids.end()) { // check burn txid is in proven txids list
if (komodo_notaries(notaries_pubkeys, block.GetHeight(), block.GetBlockTime()) >= 0) {
// check it is a notary who signed approved tx:
int i;
for (i = 0; i < sizeof(notaries_pubkeys) / sizeof(notaries_pubkeys[0]); i++) {
std::vector<uint8_t> vnotarypubkey(notaries_pubkeys[i], notaries_pubkeys[i] + 33);
#ifdef TESTMODE
char test_notary_pubkey_hex[] = "029fa302968bbae81f41983d2ec20445557b889d31227caec5d910d19b7510ef86";
uint8_t test_notary_pubkey33[33];
decode_hex(test_notary_pubkey33, 33, test_notary_pubkey_hex);
#endif
if (CheckVinPubKey(notarytx, 0, notaries_pubkeys[i]) // is signed by a notary?
&& std::find(alreadySigned.begin(), alreadySigned.end(), vnotarypubkey) == alreadySigned.end() // check if notary not re-used
#ifdef TESTMODE
|| CheckVinPubKey(notarytx, 0, test_notary_pubkey33) // test
#endif
)
{
alreadySigned.push_back(vnotarypubkey);
count++;
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "CheckNotariesApproval() notary approval checked, count=" << count << std::endl);
break;
}
}
if (i == sizeof(notaries_pubkeys) / sizeof(notaries_pubkeys[0]))
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "CheckNotariesApproval() txproof not signed by a notary or reused" << std::endl);
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() cannot get current notaries pubkeys" << std::endl);
}
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() burntxid not found in txoutproof or incorrect txoutproof" << std::endl);
}
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not unmarshal merkleBlock" << std::endl);
}
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not unmarshal txoutproof" << std::endl);
}
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() no opret in the notary tx" << std::endl);
}
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not load notary tx" << std::endl);
}
}
bool retcode;
#ifdef TESTMODE
if (count < 1) { // 1 for test
#else
if (count < 5) {
#endif
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() not enough signed notary transactions=" << count << std::endl);
retcode = false;
}
else
retcode = true;
return retcode;
}
/*
* On assetchain

2
src/crosschain.h

@ -43,6 +43,6 @@ void CompleteImportTransaction(CTransaction &importTx,int32_t offset);
/* On assetchain */
bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom);
bool CheckNotariesApproval(uint256 burntxid, const std::vector<uint256> & notaryTxids);
#endif /* CROSSCHAIN_H */

205
src/importcoin.cpp

@ -24,28 +24,63 @@
#include "script/sign.h"
#include "wallet/wallet.h"
#include "cc/CCinclude.h"
int32_t komodo_nextheight();
CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride)
// makes import tx for either coins or tokens
CTransaction MakeImportCoinTransaction(const ImportProof &proof, const CTransaction &burnTx, const std::vector<CTxOut> &payouts, uint32_t nExpiryHeightOverride)
{
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
//std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
CScript scriptSig;
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
if (mtx.fOverwintered)
mtx.nExpiryHeight = 0;
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload));
mtx.vout = payouts;
auto importData = E_MARSHAL(ss << proof; ss << burnTx);
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData));
if (nExpiryHeightOverride != 0)
mtx.nExpiryHeight = nExpiryHeightOverride; //this is for construction of the tx used for validating importtx
if (mtx.vout.size() == 0)
return CTransaction(mtx);
// add special import tx vin:
scriptSig << E_MARSHAL(ss << EVAL_IMPORTCOIN); // simple payload for coins
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), scriptSig));
if (nExpiryHeightOverride != 0)
mtx.nExpiryHeight = nExpiryHeightOverride; //this is for validation code, to make a tx used for validating the import tx
auto importData = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); // added evalcode to differentiate importdata from token opret
// if it is tokens:
vscript_t vopret;
GetOpReturnData(mtx.vout.back().scriptPubKey, vopret);
if (!vopret.empty()) {
std::vector<uint8_t> vorigpubkey;
uint8_t funcId;
std::vector <std::pair<uint8_t, vscript_t>> oprets;
std::string name, desc;
if (DecodeTokenCreateOpRet(mtx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 'c') { // parse token 'c' opret
mtx.vout.pop_back(); //remove old token opret
oprets.push_back(std::make_pair(OPRETID_IMPORTDATA, importData));
mtx.vout.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make new token 'c' opret with importData
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeImportCoinTransaction() incorrect token import opret" << std::endl);
}
}
else { //no opret in coin payouts
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData)); // import tx's opret now is in the vout's tail
}
return CTransaction(mtx);
}
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof)
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string &targetSymbol, const std::vector<CTxOut> &payouts, const std::vector<uint8_t> &rawproof)
{
std::vector<uint8_t> opret;
opret = E_MARSHAL(ss << VARINT(targetCCid);
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; // should mark burn opret to differentiate it from token opret
ss << VARINT(targetCCid);
ss << targetSymbol;
ss << SerializeHash(payouts);
ss << rawproof);
@ -87,32 +122,103 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb
}
bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,
std::vector<CTxOut> &payouts)
bool UnmarshalImportTx(const CTransaction &importTx, ImportProof &proof, CTransaction &burnTx, std::vector<CTxOut> &payouts)
{
std::vector<uint8_t> vData;
GetOpReturnData(importTx.vout[importTx.vout.size()-1].scriptPubKey, vData);
if (importTx.vout.size() < 1) return false;
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end()-1);
return importTx.vin.size() == 1 &&
importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) &&
E_UNMARSHAL(vData, ss >> proof; ss >> burnTx);
if (importTx.vout.size() < 1)
return false;
if (importTx.vin.size() != 1 || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() incorrect import tx vin" << std::endl);
return false;
}
std::vector<uint8_t> vImportData;
GetOpReturnData(importTx.vout.back().scriptPubKey, vImportData);
if (vImportData.empty()) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() no opret" << std::endl);
return false;
}
if (vImportData.begin()[0] == EVAL_TOKENS) { // if it is tokens
// get import data after token opret:
std::vector<std::pair<uint8_t, vscript_t>> oprets;
std::vector<uint8_t> vorigpubkey;
std::string name, desc;
if (DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 0) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not decode token opret" << std::endl);
return false;
}
GetOpretBlob(oprets, OPRETID_IMPORTDATA, vImportData); // fetch import data after token opret
for (std::vector<std::pair<uint8_t, vscript_t>>::const_iterator i = oprets.begin(); i != oprets.end(); i++)
if ((*i).first == OPRETID_IMPORTDATA) {
oprets.erase(i); // remove import data from token opret to restore original payouts:
break;
}
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end()-1); //exclude opret with import data
payouts.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make original payouts token opret (without import data)
}
else {
//payouts = std::vector<CTxOut>(importTx.vout.begin()+1, importTx.vout.end()); // see next
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end() - 1); // skip opret; and it is now in the back
}
uint8_t evalCode;
bool retcode = E_UNMARSHAL(vImportData, ss >> evalCode; ss >> proof; ss >> burnTx);
if (!retcode)
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not unmarshal import data" << std::endl);
return retcode;
}
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t>&rawproof)
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t>&rawproof)
{
std::vector<uint8_t> burnOpret; uint32_t ccid = 0; bool isEof=true;
std::vector<uint8_t> vburnOpret; uint32_t ccid = 0;
uint8_t evalCode;
if (burnTx.vout.size() == 0) return false;
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret);
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
ss >> targetSymbol;
ss >> payoutsHash;
ss >> rawproof; isEof=ss.eof();) || !isEof;
if (burnTx.vout.size() == 0)
return false;
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret);
if (vburnOpret.empty()) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal burn tx: empty burn opret" << std::endl);
return false;
}
if (vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
std::vector<std::pair<uint8_t, vscript_t>> oprets;
uint256 tokenid;
uint8_t evalCodeInOpret;
std::vector<CPubKey> voutTokenPubkeys;
if (DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 't')
return false;
//skip token opret:
GetOpretBlob(oprets, OPRETID_BURNDATA, vburnOpret); // fetch burnOpret after token opret
if (vburnOpret.empty()) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal token burn tx: empty burn opret for tokenid=" << tokenid.GetHex() << std::endl);
return false;
}
}
if (vburnOpret.begin()[0] == EVAL_IMPORTCOIN) {
uint8_t evalCode;
return E_UNMARSHAL(vburnOpret, ss >> evalCode;
ss >> VARINT(*targetCCid);
ss >> targetSymbol;
ss >> payoutsHash;
ss >> rawproof);
}
else {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() invalid eval code in opret" << std::endl);
return false;
}
}
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt)
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &srcaddr, std::string &receipt)
{
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true;
std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash;
@ -155,16 +261,55 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPu
*/
CAmount GetCoinImportValue(const CTransaction &tx)
{
TxProof proof;
ImportProof proof;
CTransaction burnTx;
std::vector<CTxOut> payouts;
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
return burnTx.vout.size() ? burnTx.vout.back().nValue : 0;
bool isNewImportTx = false;
if ((isNewImportTx = UnmarshalImportTx(tx, proof, burnTx, payouts))) {
if (burnTx.vout.size() > 0) {
vscript_t vburnOpret;
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret);
if (vburnOpret.empty()) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetCoinImportValue() empty burn opret" << std::endl);
return 0;
}
if (isNewImportTx && vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
uint8_t evalCodeInOpret;
uint256 tokenid;
std::vector<CPubKey> voutTokenPubkeys;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
return 0;
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init as if no non-fungibles
vscript_t vnonfungibleOpret;
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
if (!vnonfungibleOpret.empty())
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
// calc outputs for burn tx
int64_t 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;
return ccBurnOutputs + burnTx.vout.back().nValue; // total token burned value
}
else
return burnTx.vout.back().nValue; // coin burned value
}
}
return 0;
}
/*
* CoinImport is different enough from normal script execution that it's not worth
* making all the mods neccesary in the interpreter to do the dispatch correctly.

79
src/importcoin.h

@ -22,13 +22,79 @@
#include "script/interpreter.h"
#include <cryptoconditions.h>
enum ProofKind : uint8_t {
PROOF_NONE = 0x00,
PROOF_MERKLEBRANCH = 0x11,
PROOF_NOTARYTXIDS = 0x12,
PROOF_MERKLEBLOCK = 0x13
};
class ImportProof {
private:
uint8_t proofKind;
TxProof proofBranch;
std::vector<uint256> notaryTxids;
std::vector<uint8_t> proofBlock;
public:
ImportProof() { proofKind = PROOF_NONE; }
ImportProof(const TxProof &_proofBranch) {
proofKind = PROOF_MERKLEBRANCH; proofBranch = _proofBranch;
}
ImportProof(const std::vector<uint256> &_notaryTxids) {
proofKind = PROOF_NOTARYTXIDS; notaryTxids = _notaryTxids;
}
ImportProof(const std::vector<uint8_t> &_proofBlock) {
proofKind = PROOF_MERKLEBLOCK; proofBlock = _proofBlock;
}
ADD_SERIALIZE_METHODS
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(proofKind);
if (proofKind == PROOF_MERKLEBRANCH)
READWRITE(proofBranch);
else if (proofKind == PROOF_NOTARYTXIDS)
READWRITE(notaryTxids);
else if (proofKind == PROOF_MERKLEBLOCK)
READWRITE(proofBlock);
}
bool IsMerkleBranch(TxProof &_proofBranch) const {
if (proofKind == PROOF_MERKLEBRANCH) {
_proofBranch = proofBranch;
return true;
}
else
return false;
}
bool IsNotaryTxids(std::vector<uint256> &_notaryTxids) const {
if (proofKind == PROOF_NOTARYTXIDS) {
_notaryTxids = notaryTxids;
return true;
}
else
return false;
}
bool IsMerkleBlock(std::vector<uint8_t> &_proofBlock) const {
if (proofKind == PROOF_MERKLEBLOCK) {
_proofBlock = proofBlock;
return true;
}
else
return false;
}
};
CAmount GetCoinImportValue(const CTransaction &tx);
CTransaction MakeImportCoinTransaction(const TxProof proof,
const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride = 0);
CTransaction MakeImportCoinTransaction(const ImportProof &proof, const CTransaction &burnTx, const std::vector<CTxOut> &payouts, uint32_t nExpiryHeightOverride = 0);
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof);
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string &targetSymbol, const std::vector<CTxOut> &payouts, const std::vector<uint8_t> &rawproof);
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256>txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount);
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,std::string srcaddr,
@ -37,7 +103,7 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t> &rawproof);
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt);
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPubKey> &publishers,std::vector<uint256> &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount);
bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,std::vector<CTxOut> &payouts);
bool UnmarshalImportTx(const CTransaction &importTx, ImportProof &proof, CTransaction &burnTx,std::vector<CTxOut> &payouts);
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state);
@ -45,4 +111,9 @@ void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, i
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs);
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs);
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33]);
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount);
int32_t GetSelfimportProof(const CMutableTransaction &sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull);
#endif /* IMPORTCOIN_H */

690
src/rpc/crosschain.cpp

@ -38,6 +38,7 @@
#include "key_io.h"
#include "cc/CCImportGateway.h"
#include "cc/CCtokens.h"
#include <stdint.h>
#include <univalue.h>
@ -52,6 +53,8 @@ extern std::string CCerror;
extern std::string ASSETCHAINS_SELFIMPORT;
extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT;
int32_t ensure_CCrequirements(uint8_t evalcode);
bool EnsureWalletIsAvailable(bool avoidException);
int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip);
int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height);
@ -60,8 +63,8 @@ uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
extern std::string ASSETCHAINS_SELFIMPORT;
std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx);
int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount);
//std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx);
//int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount);
std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector<CTxOut> vouts);
UniValue assetchainproof(const UniValue& params, bool fHelp)
@ -185,7 +188,7 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
"If neccesary, the transaction should be funded using fundrawtransaction.\n"
"Finally, the transaction should be signed using signrawtransaction\n"
"The finished export transaction, plus the payouts, should be passed to "
"the \"migrate_createimporttransaction\" method on a KMD node to get the corresponding "
"the \"migrate_createimporttransaction\" method to get the corresponding "
"import transaction.\n"
);
@ -206,19 +209,30 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
if (strcmp(ASSETCHAINS_SYMBOL,targetSymbol.c_str()) == 0)
throw runtime_error("cant send a coin to the same chain");
/// Tested 44 vins p2pkh inputs as working. Set this at 25, but its a tx size limit.
// likely with a single RPC you can limit it by the size of tx.
if (tx.vout.size() > 25)
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot have more than 50 vins, transaction too large.");
CAmount burnAmount = 0;
for (int i=0; i<tx.vout.size(); i++) burnAmount += tx.vout[i].nValue;
if (burnAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export a negative or zero value.");
// This is due to MAX MONEY in target. We set it at min 1 million coins, so you cant export more than 1 million,
// without knowing the MAX money on the target this was the easiest solution.
if (burnAmount > 1000000LL*COIN)
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export more than 1 million coins per export.");
/* note: we marshal to rawproof in a different way (to be able to add other objects)
rawproof.resize(strlen(ASSETCHAINS_SYMBOL));
ptr = rawproof.data();
for (i=0; i<rawproof.size(); i++)
ptr[i] = ASSETCHAINS_SYMBOL[i];
ptr[i] = ASSETCHAINS_SYMBOL[i]; */
const std::string chainSymbol(ASSETCHAINS_SYMBOL);
rawproof = E_MARSHAL(ss << chainSymbol); // add src chain name
CTxOut burnOut = MakeBurnOutput(burnAmount+txfee, ccid, targetSymbol, tx.vout,rawproof);
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << tx.vout))));
@ -228,26 +242,308 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
return ret;
}
// creates burn tx as an alternative to 'migrate_converttoexport()'
UniValue migrate_createburntransaction(const UniValue& params, bool fHelp)
{
UniValue ret(UniValue::VOBJ);
//uint8_t *ptr;
//uint8_t i;
uint32_t ccid = ASSETCHAINS_CC;
int64_t txfee = 10000;
if (fHelp || params.size() != 3 && params.size() != 4)
throw runtime_error(
"migrate_createburntransaction dest_symbol dest_addr amount [tokenid]\n"
"\nCreates a raw burn transaction to make a cross-chain coin or non-fungible token transfer.\n"
"The parameters:\n"
"dest_symbol destination chain ac_name\n"
"dest_addr address on the destination chain where coins are to be sent or pubkey if tokens are to be sent\n"
"amount amount in coins to be burned on the source chain and sent to the destination address/pubkey on the destination chain, for tokens should be equal to 1\n"
"tokenid token id, if tokens are transferred (optional). Only non-fungible tokens are supported\n"
"\n"
"The transaction should be sent using sendrawtransaction to the source chain\n"
"The finished burn transaction and payouts should be also passed to "
"the \"migrate_createimporttransaction\" method to get the corresponding import transaction.\n"
);
if (ASSETCHAINS_CC < KOMODO_FIRSTFUNGIBLEID)
throw runtime_error("-ac_cc < KOMODO_FIRSTFUNGIBLEID");
if (ASSETCHAINS_SYMBOL[0] == 0)
throw runtime_error("Must be called on assetchain");
// if -pubkey not set it sends change to null pubkey.
// we need a better way to return errors from this function!
if (ensure_CCrequirements(225) < 0)
throw runtime_error("You need to set -pubkey, or run setpukbey RPC, or imports are disabled on this chain.");
string targetSymbol = params[0].get_str();
if (targetSymbol.size() == 0 || targetSymbol.size() > 32)
throw runtime_error("targetSymbol length must be >0 and <=32");
if (strcmp(ASSETCHAINS_SYMBOL, targetSymbol.c_str()) == 0)
throw runtime_error("cant send a coin to the same chain");
std::string dest_addr_or_pubkey = params[1].get_str();
CAmount burnAmount;
if(params.size() == 3)
burnAmount = (CAmount)( atof(params[2].get_str().c_str()) * COIN + 0.00000000499999 );
else
burnAmount = atoll(params[2].get_str().c_str());
if (burnAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export a negative or zero value.");
if (burnAmount > 1000000LL * COIN)
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export more than 1 million coins per export.");
uint256 tokenid = zeroid;
if( params.size() == 4 )
tokenid = Parseuint256(params[3].get_str().c_str());
CPubKey myPubKey = Mypubkey();
struct CCcontract_info *cpTokens, C;
cpTokens = CCinit(&C, EVAL_TOKENS);
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
const std::string chainSymbol(ASSETCHAINS_SYMBOL);
std::vector<uint8_t> rawproof; //(chainSymbol.begin(), chainSymbol.end());
if (tokenid.IsNull()) { // coins
int64_t inputs;
if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 60)) == 0) {
throw runtime_error("Cannot find normal inputs\n");
}
CTxDestination txdest = DecodeDestination(dest_addr_or_pubkey.c_str());
CScript scriptPubKey = GetScriptForDestination(txdest);
if (!scriptPubKey.IsPayToPublicKeyHash()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Incorrect destination addr.");
}
mtx.vout.push_back(CTxOut(burnAmount, scriptPubKey)); // 'model' vout
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << mtx.vout)))); // save 'model' vout
rawproof = E_MARSHAL(ss << chainSymbol); // add src chain name
CTxOut burnOut = MakeBurnOutput(burnAmount+txfee, ccid, targetSymbol, mtx.vout, rawproof); //make opret with burned amount
mtx.vout.clear(); // remove 'model' vout
int64_t change = inputs - (burnAmount+txfee);
if (change != 0)
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); // make change here to prevent it from making in FinalizeCCtx
mtx.vout.push_back(burnOut); // mtx now has only burned vout (that is, amount sent to OP_RETURN making it unspendable)
//std::string exportTxHex = FinalizeCCTx(0, cpTokens, mtx, myPubKey, txfee, CScript()); // no change no opret
}
else { // tokens
CTransaction tokenbasetx;
uint256 hashBlock;
vscript_t vopretNonfungible;
vscript_t vopretBurnData;
std::vector<uint8_t> vorigpubkey, vdestpubkey;
std::string name, description;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock))
throw runtime_error("Could not load token creation tx\n");
// check if it is non-fungible tx and get its second evalcode from non-fungible payload
if (tokenbasetx.vout.size() == 0)
throw runtime_error("No vouts in token tx\n");
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) != 'c')
throw runtime_error("Incorrect token creation tx\n");
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
/* allow fungible tokens:
if (vopretNonfungible.empty())
throw runtime_error("No non-fungible token data\n"); */
uint8_t destEvalCode = EVAL_TOKENS;
if (!vopretNonfungible.empty())
destEvalCode = vopretNonfungible.begin()[0];
// check non-fungible tokens amount
if (!vopretNonfungible.empty() && burnAmount != 1)
throw JSONRPCError(RPC_TYPE_ERROR, "For non-fungible tokens amount should be equal to 1.");
vdestpubkey = ParseHex(dest_addr_or_pubkey);
CPubKey destPubKey = pubkey2pk(vdestpubkey);
if (!destPubKey.IsValid())
throw runtime_error("Invalid destination pubkey\n");
int64_t inputs;
if ((inputs = AddNormalinputs(mtx, myPubKey, txfee, 1)) == 0) // for miners in dest chain
throw runtime_error("No normal input found for two txfee\n");
int64_t ccInputs;
if ((ccInputs = AddTokenCCInputs(cpTokens, mtx, myPubKey, tokenid, burnAmount, 4)) < burnAmount)
throw runtime_error("No token inputs found (please try to consolidate tokens)\n");
// make payouts (which will be in the import tx with token):
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // new marker to token cc addr, burnable and validated, vout position now changed to 0 (from 1)
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, destPubKey));
std::vector<std::pair<uint8_t, vscript_t>> voprets;
if (!vopretNonfungible.empty())
voprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); // add additional opret with non-fungible data
mtx.vout.push_back(CTxOut((CAmount)0, EncodeTokenCreateOpRet('c', vorigpubkey, name, description, voprets))); // make token import opret
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << mtx.vout)))); // save payouts for import tx
rawproof = E_MARSHAL(ss << chainSymbol << tokenbasetx); // add src chain name and token creation tx
CTxOut burnOut = MakeBurnOutput(0, ccid, targetSymbol, mtx.vout, rawproof); //make opret with amount=0 because tokens are burned, not coins (see next vout)
mtx.vout.clear(); // remove payouts
// now make burn transaction:
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, pubkey2pk(ParseHex(CC_BURNPUBKEY)))); // burn tokens
int64_t change = inputs - txfee;
if (change != 0)
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); // make change here to prevent it from making in FinalizeCCtx
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); // maybe we do not need this because ccTokens has the const for burn pubkey
int64_t ccChange = ccInputs - burnAmount;
if (ccChange != 0)
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, ccChange, myPubKey));
GetOpReturnData(burnOut.scriptPubKey, vopretBurnData);
mtx.vout.push_back(CTxOut(txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_BURNDATA, vopretBurnData)))); //burn txfee for miners in dest chain
}
std::string burnTxHex = FinalizeCCTx(0, cpTokens, mtx, myPubKey, txfee, CScript()); //no change, no opret
ret.push_back(Pair("BurnTxHex", burnTxHex));
return ret;
}
// util func to check burn tx and source chain params
void CheckBurnTxSource(uint256 burntxid, UniValue &info) {
CTransaction burnTx;
uint256 blockHash;
if (!GetTransaction(burntxid, burnTx, blockHash, true))
throw std::runtime_error("Cannot find burn transaction");
if (blockHash.IsNull())
throw std::runtime_error("Burn tx still in mempool");
uint256 payoutsHash;
std::string targetSymbol;
uint32_t targetCCid;
std::vector<uint8_t> rawproof;
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof))
throw std::runtime_error("Cannot unmarshal burn tx data");
vscript_t vopret;
std::string sourceSymbol;
CTransaction tokenbasetxStored;
uint256 tokenid = zeroid;
if (burnTx.vout.size() > 0 && GetOpReturnData(burnTx.vout.back().scriptPubKey, vopret) && !vopret.empty()) {
if (vopret.begin()[0] == EVAL_TOKENS) {
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbasetxStored))
throw std::runtime_error("Cannot unmarshal rawproof for tokens");
uint8_t evalCode;
std::vector<CPubKey> voutPubkeys;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if( DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCode, tokenid, voutPubkeys, oprets) == 0 )
throw std::runtime_error("Cannot decode token opret in burn tx");
if( tokenid != tokenbasetxStored.GetHash() )
throw std::runtime_error("Incorrect tokenbase in burn tx");
CTransaction tokenbasetx;
uint256 hashBlock;
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
throw std::runtime_error("Could not load tokenbase tx");
}
// check if nonfungible data present
if (tokenbasetx.vout.size() > 0) {
std::vector<uint8_t> origpubkey;
std::string name, description;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
vscript_t vopretNonfungible;
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') {
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
if (vopretNonfungible.empty())
throw std::runtime_error("Could not migrate fungible tokens");
}
else
throw std::runtime_error("Could not decode opreturn in tokenbase tx");
}
else
throw std::runtime_error("Incorrect tokenbase tx: not opreturn");
struct CCcontract_info *cpTokens, CCtokens_info;
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
int64_t ccInputs = 0, ccOutputs = 0;
if( !TokensExactAmounts(true, cpTokens, ccInputs, ccOutputs, NULL, burnTx, tokenid) )
throw std::runtime_error("Incorrect token burn tx: cc inputs <> cc outputs");
}
else if (vopret.begin()[0] == EVAL_IMPORTCOIN) {
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol))
throw std::runtime_error("Cannot unmarshal rawproof for coins");
}
else
throw std::runtime_error("Incorrect eval code in opreturn");
}
else
throw std::runtime_error("No opreturn in burn tx");
if (sourceSymbol != ASSETCHAINS_SYMBOL)
throw std::runtime_error("Incorrect source chain in rawproof");
if (targetCCid != ASSETCHAINS_CC)
throw std::runtime_error("Incorrect CCid in burn tx");
if (targetSymbol == ASSETCHAINS_SYMBOL)
throw std::runtime_error("Must not be called on the destination chain");
// fill info to return for the notary operator (if manual notarization) or user
info.push_back(Pair("SourceSymbol", sourceSymbol));
info.push_back(Pair("TargetSymbol", targetSymbol));
info.push_back(Pair("TargetCCid", std::to_string(targetCCid)));
if (!tokenid.IsNull())
info.push_back(Pair("tokenid", tokenid.GetHex()));
}
/*
* The process to migrate funds
* The process to migrate funds from a chain to chain
*
* Create a transaction on assetchain:
* 1.Create a transaction on assetchain (deprecated):
* 1.1 generaterawtransaction
* 1.2 migrate_converttoexport
* 1.3 fundrawtransaction
* 1.4 signrawtransaction
*
* generaterawtransaction
* migrate_converttoexport
* fundrawtransaction
* signrawtransaction
* alternatively, burn (export) transaction may be created with this new rpc call:
* 1. migrate_createburntransaction
*
* migrate_createimportransaction
* migrate_completeimporttransaction
* next steps:
* 2. migrate_createimporttransaction
* 3. migrate_completeimporttransaction
*/
UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error("migrate_createimporttransaction burnTx payouts\n\n"
"Create an importTx given a burnTx and the corresponding payouts, hex encoded");
if (fHelp || params.size() < 2)
throw runtime_error("migrate_createimporttransaction burnTx payouts [notarytxid-1]..[notarytxid-N]\n\n"
"Create an importTx given a burnTx and the corresponding payouts, hex encoded\n"
"optional notarytxids are txids of notary operator proofs of burn tx existense (from destination chain).\n"
"Do not make subsequent call to migrate_completeimporttransaction if notary txids are set");
if (ASSETCHAINS_CC < KOMODO_FIRSTFUNGIBLEID)
throw runtime_error("-ac_cc < KOMODO_FIRSTFUNGIBLEID");
@ -261,27 +557,52 @@ UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
if (!E_UNMARSHAL(txData, ss >> burnTx))
throw runtime_error("Couldn't parse burnTx");
if( burnTx.vin.size() == 0 )
throw runtime_error("No vins in the burnTx");
if (burnTx.vout.size() == 0)
throw runtime_error("No vouts in the burnTx");
vector<CTxOut> payouts;
if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts))
throw runtime_error("Couldn't parse payouts");
uint256 txid = burnTx.GetHash();
TxProof proof = GetAssetchainProof(burnTx.GetHash(),burnTx);
ImportProof importProof;
if (params.size() == 2) { // standard MoMoM based notarization
// get MoM import proof
importProof = ImportProof(GetAssetchainProof(burnTx.GetHash(), burnTx));
}
else { // notarization by manual operators notary tx
UniValue info(UniValue::VOBJ);
CheckBurnTxSource(burnTx.GetHash(), info);
// get notary import proof
std::vector<uint256> notaryTxids;
for (int i = 2; i < params.size(); i++) {
uint256 txid = Parseuint256(params[i].get_str().c_str());
if (txid.IsNull())
throw runtime_error("Incorrect notary approval txid");
notaryTxids.push_back(txid);
}
importProof = ImportProof(notaryTxids);
}
CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
CTransaction importTx = MakeImportCoinTransaction(importProof, burnTx, payouts);
return HexStr(E_MARSHAL(ss << importTx));
std::string importTxHex = HexStr(E_MARSHAL(ss << importTx));
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("ImportTxHex", importTxHex));
return ret;
}
UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error("migrate_completeimporttransaction importTx (offset)\n\n"
throw runtime_error("migrate_completeimporttransaction importTx [offset]\n\n"
"Takes a cross chain import tx with proof generated on assetchain "
"and extends proof to target chain proof root\n"
"offset is optional, use it to increase the used KMD height, use when import fails on target.");
"offset is optional, use it to increase the used KMD height, use when import fails.");
if (ASSETCHAINS_SYMBOL[0] != 0)
throw runtime_error("Must be called on KMD");
@ -289,67 +610,130 @@ UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
CTransaction importTx;
if (!E_UNMARSHAL(ParseHexV(params[0], "argument 1"), ss >> importTx))
throw runtime_error("Couldn't parse importTx");
int32_t offset = 0;
if ( params.size() == 2 )
offset = params[1].get_int();
CompleteImportTransaction(importTx, offset);
return HexStr(E_MARSHAL(ss << importTx));
std::string importTxHex = HexStr(E_MARSHAL(ss << importTx));
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("ImportTxHex", importTxHex));
return ret;
}
/*
* Alternate coin migration solution if MoMoM migration has failed
*
* The workflow:
* On the source chain user calls migrate_createburntransaction, sends the burn tx to the chain and sends its txid and the source chain name to the notary operators (off-chain)
* the notary operators call migrate_checkburntransactionsource on the source chain
* on the destination chain the notary operators call migrate_createnotaryapprovaltransaction and pass the burn txid and txoutproof received from the previous call,
* the notary operators send the approval transactions to the chain and send their txids to the user (off-chain)
* on the source chain the user calls migrate_createimporttransaction and passes to it notary txids as additional parameters
* then the user sends the import transaction to the destination chain (where the notary approvals will be validated)
*/
// checks if burn tx exists and params stored in the burn tx match to the source chain
// returns txproof
// run it on the source chain
UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error("migrate_checkburntransactionsource burntxid\n\n"
"checks if params stored in the burn tx match to its tx chain");
if (ASSETCHAINS_SYMBOL[0] == 0)
throw runtime_error("Must be called on asset chain");
uint256 burntxid = Parseuint256(params[0].get_str().c_str());
UniValue result(UniValue::VOBJ);
CheckBurnTxSource(burntxid, result); // check and get burn tx data
// get tx proof for burn tx
UniValue nextparams(UniValue::VARR);
UniValue txids(UniValue::VARR);
txids.push_back(burntxid.GetHex());
nextparams.push_back(txids);
result.push_back(Pair("TxOutProof", gettxoutproof(nextparams, false))); // get txoutproof
result.push_back(Pair("result", "success")); // get txoutproof
return result;
}
// creates a tx for the dest chain with txproof
// used as a momom-backup manual import solution
// run it on the dest chain
UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error("migrate_createnotaryapprovaltransaction burntxid txoutproof\n\n"
"Creates a tx for destination chain with burn tx proof\n"
"txoutproof should be retrieved by komodo-cli migrate_checkburntransactionsource call on the source chain\n" );
if (ASSETCHAINS_SYMBOL[0] == 0)
throw runtime_error("Must be called on asset chain");
uint256 burntxid = Parseuint256(params[0].get_str().c_str());
if (burntxid.IsNull())
throw runtime_error("Couldn't parse burntxid or it is null");
std::vector<uint8_t> proofData = ParseHex(params[1].get_str());
CMerkleBlock merkleBlock;
std::vector<uint256> prooftxids;
if (!E_UNMARSHAL(proofData, ss >> merkleBlock))
throw runtime_error("Couldn't parse txoutproof");
merkleBlock.txn.ExtractMatches(prooftxids);
if (std::find(prooftxids.begin(), prooftxids.end(), burntxid) == prooftxids.end())
throw runtime_error("No burntxid in txoutproof");
const int64_t txfee = 10000;
struct CCcontract_info *cpDummy, C;
cpDummy = CCinit(&C, EVAL_TOKENS); // just for FinalizeCCtx to work
// creating a tx with proof:
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
if (AddNormalinputs(mtx, Mypubkey(), txfee*2, 4) == 0)
throw runtime_error("Cannot find normal inputs\n");
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG));
std::string notaryTxHex = FinalizeCCTx(0, cpDummy, mtx, Mypubkey(), txfee, CScript() << OP_RETURN << E_MARSHAL(ss << proofData;));
UniValue result(UniValue::VOBJ);
result.push_back(Pair("NotaryTxHex", notaryTxHex));
return result;
}
// creates a source 'quasi-burn' tx for AC_PUBKEY
// run it on the same asset chain
UniValue selfimport(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ);
CMutableTransaction sourceMtx, templateMtx;
std::string destaddr;
std::string source;
std::string rawsourcetx;
std::string sourceTxHex;
std::string importTxHex;
CTransaction burnTx;
CTxOut burnOut;
uint64_t burnAmount;
uint256 sourcetxid, blockHash;
std::vector<CTxOut> vouts;
std::vector<uint8_t> rawproof, rawproofEmpty;
int32_t ivout = 0;
CScript scriptPubKey;
TxProof proof;
std::vector<uint8_t> rawproof;
if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
throw runtime_error("selfimport only works on -ac_import chains");
if (fHelp || params.size() != 2)
throw runtime_error("selfimport destaddr amount\n"
//old: "selfimport rawsourcetx sourcetxid {nvout|\"find\"} amount \n"
//TODO: "or selfimport rawburntx burntxid {nvout|\"find\"} rawproof source bindtxid height} \n"
"\ncreates self import coin transaction");
/* OLD selfimport schema:
rawsourcetx = params[0].get_str();
sourcetxid = Parseuint256((char *)params[1].get_str().c_str()); // allow for txid != hash(rawtx)
int32_t ivout = -1;
if( params[2].get_str() != "find" ) {
if( !std::all_of(params[2].get_str().begin(), params[2].get_str().end(), ::isdigit) ) // check if not all chars are digit
throw std::runtime_error("incorrect nvout param");
ivout = atoi(params[2].get_str().c_str());
}
burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; */
destaddr = params[0].get_str();
burnAmount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999;
source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param
/* TODO for gateways:
if ( params.size() >= 5 )
{
rawproof = ParseHex(params[4].get_str().c_str());
if ( params.size() == 6 )
source = params[5].get_str();
} */
if (source == "BEAM")
{
@ -369,51 +753,34 @@ UniValue selfimport(const UniValue& params, bool fHelp)
}
else if (source == "PUBKEY")
{
ImportProof proofNull;
CTxDestination dest = DecodeDestination(destaddr.c_str());
rawsourcetx = MakeSelfImportSourceTx(dest, burnAmount, sourceMtx);
sourcetxid = sourceMtx.GetHash();
CMutableTransaction sourceMtx = MakeSelfImportSourceTx(dest, burnAmount); // make self-import source tx
vscript_t rawProofEmpty;
CMutableTransaction templateMtx;
// prepare self-import 'quasi-burn' tx and also create vout for import tx (in mtx.vout):
if (GetSelfimportProof(source, templateMtx, scriptPubKey, proof, rawsourcetx, ivout, sourcetxid, burnAmount) < 0)
throw std::runtime_error("Failed validating selfimport");
if (GetSelfimportProof(sourceMtx, templateMtx, proofNull) < 0)
throw std::runtime_error("Failed creating selfimport template tx");
vouts = templateMtx.vout;
burnOut = MakeBurnOutput(burnAmount, 0xffffffff, ASSETCHAINS_SELFIMPORT, vouts, rawproofEmpty);
burnOut = MakeBurnOutput(burnAmount, 0xffffffff, ASSETCHAINS_SELFIMPORT, vouts, rawProofEmpty);
templateMtx.vout.clear();
templateMtx.vout.push_back(burnOut); // burn tx has only opret with vouts and optional proof
burnTx = templateMtx; // complete the creation of 'quasi-burn' tx
std::string hextx = HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proof, burnTx, vouts)));
CTxDestination address;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
result.push_back(Pair("sourceTxHex", rawsourcetx));
result.push_back(Pair("importTxHex", hextx));
result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx
result.push_back(Pair("DestinationAddress", EncodeDestination(address))); // notify user about the address where the funds will be sent
sourceTxHex = HexStr(E_MARSHAL(ss << sourceMtx));
importTxHex = HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proofNull, burnTx, vouts)));
result.push_back(Pair("SourceTxHex", sourceTxHex));
result.push_back(Pair("ImportTxHex", importTxHex));
return result;
}
else if (source == ASSETCHAINS_SELFIMPORT)
{
throw std::runtime_error("not implemented yet\n");
if (params.size() != 8)
throw runtime_error("use \'selfimport rawburntx burntxid nvout rawproof source bindtxid height\' to import from a coin chain\n");
uint256 bindtxid = Parseuint256((char *)params[6].get_str().c_str());
int32_t height = atoi((char *)params[7].get_str().c_str());
// source is external coin is the assetchains symbol in the burnTx OP_RETURN
// burnAmount, rawtx and rawproof should be enough for gatewaysdeposit equivalent
//std::string hextx = MakeGatewaysImportTx(0, bindtxid, height, source, rawproof, rawsourcetx, ivout, "");
// result.push_back(Pair("hex", hextx));
// result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx
return -1;
}
return result;
}
@ -939,14 +1306,15 @@ UniValue getimports(const UniValue& params, bool fHelp)
{
UniValue objTx(UniValue::VOBJ);
objTx.push_back(Pair("txid",tx.GetHash().ToString()));
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
TotalImported += tx.vout[0].nValue;
objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[0].nValue)));
if (ExtractDestination(tx.vout[0].scriptPubKey, importaddress))
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
TotalImported += tx.vout[1].nValue;
objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[1].nValue)));
if (ExtractDestination(tx.vout[1].scriptPubKey, importaddress))
{
objTx.push_back(Pair("address", CBitcoinAddress(importaddress).ToString()));
}
UniValue objBurnTx(UniValue::VOBJ);
UniValue objBurnTx(UniValue::VOBJ);
CPubKey vinPubkey;
if (UnmarshalImportTx(tx, proof, burnTx, payouts))
{
if (burnTx.vout.size() == 0)
@ -959,8 +1327,14 @@ UniValue getimports(const UniValue& params, bool fHelp)
{
if (rawproof.size() > 0)
{
std::string sourceSymbol(rawproof.begin(), rawproof.end());
std::string sourceSymbol;
CTransaction tokenbasetx;
E_UNMARSHAL(rawproof, ss >> sourceSymbol;
if (!ss.eof())
ss >> tokenbasetx );
objBurnTx.push_back(Pair("source", sourceSymbol));
if( !tokenbasetx.IsNull() )
objBurnTx.push_back(Pair("tokenid", tokenbasetx.GetHash().GetHex()));
}
}
}
@ -973,3 +1347,137 @@ UniValue getimports(const UniValue& params, bool fHelp)
result.push_back(Pair("time", block.GetBlockTime()));
return result;
}
// outputs burn transactions in the wallet
UniValue getwalletburntransactions(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
"getwalletburntransactions \"count\"\n\n"
"Lists most recent wallet burn transactions up to \'count\' parameter\n"
"parameter \'count\' is optional. If omitted, defaults to 10 burn transactions"
"\n\n"
"\nResult:\n"
"[\n"
" {\n"
" \"txid\": (string)\n"
" \"burnedAmount\" : (numeric)\n"
" \"targetSymbol\" : (string)\n"
" \"targetCCid\" : (numeric)\n"
" }\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletburntransactions", "100")
+ HelpExampleRpc("getwalletburntransactions", "100")
+ HelpExampleCli("getwalletburntransactions", "")
+ HelpExampleRpc("getwalletburntransactions", "")
);
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
LOCK2(cs_main, pwalletMain->cs_wallet);
string strAccount = "*";
isminefilter filter = ISMINE_SPENDABLE;
int nCount = 10;
if (params.size() == 1)
nCount = atoi(params[0].get_str());
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
UniValue ret(UniValue::VARR);
std::list<CAccountingEntry> acentries;
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
// iterate backwards until we have nCount items to return:
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0)
{
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "pwtx iterpos=" << (int32_t)pwtx->nOrderPos << " txid=" << pwtx->GetHash().GetHex() << std::endl);
vscript_t vopret;
std::string targetSymbol;
uint32_t targetCCid; uint256 payoutsHash;
std::vector<uint8_t> rawproof;
if (pwtx->vout.size() > 0 && GetOpReturnData(pwtx->vout.back().scriptPubKey, vopret) && !vopret.empty() &&
UnmarshalBurnTx(*pwtx, targetSymbol, &targetCCid, payoutsHash, rawproof)) {
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("txid", pwtx->GetHash().GetHex()));
if (vopret.begin()[0] == EVAL_TOKENS) {
// get burned token value
std::vector<std::pair<uint8_t, vscript_t>> oprets;
uint256 tokenid;
uint8_t evalCodeInOpret;
std::vector<CPubKey> voutTokenPubkeys;
//skip token opret:
if (DecodeTokenOpRet(pwtx->vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 0) {
CTransaction tokenbasetx;
uint256 hashBlock;
if (myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
std::vector<uint8_t> vorigpubkey;
std::string name, description;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if (tokenbasetx.vout.size() > 0 &&
DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) == 'c')
{
uint8_t destEvalCode = EVAL_TOKENS; // init set to fungible token:
vscript_t vopretNonfungible;
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
if (!vopretNonfungible.empty())
destEvalCode = vopretNonfungible.begin()[0];
int64_t burnAmount = 0;
for (auto v : pwtx->vout)
if (v.scriptPubKey.IsPayToCryptoCondition() &&
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(destEvalCode ? destEvalCode : EVAL_TOKENS, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
burnAmount += v.nValue;
entry.push_back(Pair("burnedAmount", ValueFromAmount(burnAmount)));
entry.push_back(Pair("tokenid", tokenid.GetHex()));
}
}
}
}
else
entry.push_back(Pair("burnedAmount", ValueFromAmount(pwtx->vout.back().nValue))); // coins
entry.push_back(Pair("targetSymbol", targetSymbol));
entry.push_back(Pair("targetCCid", std::to_string(targetCCid)));
if (mytxid_inmempool(pwtx->GetHash()))
entry.push_back(Pair("inMempool", "yes"));
ret.push_back(entry);
}
} //else fprintf(stderr,"null pwtx\n
if ((int)ret.size() >= (nCount))
break;
}
// ret is newest to oldest
if (nCount > (int)ret.size())
nCount = ret.size();
vector<UniValue> arrTmp = ret.getValues();
vector<UniValue>::iterator first = arrTmp.begin();
vector<UniValue>::iterator last = arrTmp.begin();
std::advance(last, nCount);
if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
ret.clear();
ret.setArray();
ret.push_backV(arrTmp);
return ret;
}

3
src/rpc/server.cpp

@ -350,9 +350,12 @@ static const CRPCCommand vRPCCommands[] =
{ "crosschain", "getNotarisationsForBlock", &getNotarisationsForBlock, true },
{ "crosschain", "scanNotarisationsDB", &scanNotarisationsDB, true },
{ "crosschain", "getimports", &getimports, true },
{ "crosschain", "getwalletburntransactions", &getwalletburntransactions, true },
{ "crosschain", "migrate_converttoexport", &migrate_converttoexport, true },
{ "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true },
{ "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true },
{ "crosschain", "migrate_checkburntransactionsource", &migrate_checkburntransactionsource, true },
{ "crosschain", "migrate_createnotaryapprovaltransaction", &migrate_createnotaryapprovaltransaction, true },
{ "crosschain", "selfimport", &selfimport, true },
{ "crosschain", "importdual", &importdual, true },
//ImportGateway

3
src/rpc/server.h

@ -478,9 +478,12 @@ extern UniValue crosschainproof(const UniValue& params, bool fHelp);
extern UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp);
extern UniValue scanNotarisationsDB(const UniValue& params, bool fHelp);
extern UniValue getimports(const UniValue& params, bool fHelp);
extern UniValue getwalletburntransactions(const UniValue& params, bool fHelp);
extern UniValue migrate_converttoexport(const UniValue& params, bool fHelp);
extern UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp);
extern UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp);
extern UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp);
extern UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp);
extern UniValue notaries(const UniValue& params, bool fHelp);
extern UniValue minerids(const UniValue& params, bool fHelp);

Loading…
Cancel
Save