// Copyright (c) 2016-2020 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html /****************************************************************************** * 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. * * * ******************************************************************************/ #include "amount.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" #include "crosschain.h" #include "notarizationdb.h" #include "importcoin.h" #include "base58.h" #include "consensus/validation.h" #include "cc/eval.h" #include "cc/utils.h" #include "cc/CCinclude.h" #include "main.h" #include "primitives/transaction.h" #include "rpc/server.h" #include "sync.h" #include "util.h" #include "script/script.h" #include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" #include "key_io.h" #include "cc/CCImportGateway.h" #include "cc/CCtokens.h" #include #include #include using namespace std; #define RETURN_IF_ERROR(CCerror) if ( CCerror != "" ) { ERR_RESULT(CCerror); return(result); } #define ERR_RESULT(x) result.push_back(Pair("result", "error")) , result.push_back(Pair("error", x)); 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); struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); int32_t hush_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 MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts); UniValue assetchainproof(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 hash; // parse params and get notarisation data for tx if ( fHelp || params.size() != 1) throw runtime_error("assetchainproof needs a txid"); hash = uint256S(params[0].get_str()); CTransaction tx; auto proof = GetAssetchainProof(hash,tx); auto proofData = E_MARSHAL(ss << proof); return HexStr(proofData); } UniValue crosschainproof(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue ret(UniValue::VOBJ); //fprintf(stderr,"crosschainproof needs to be implemented\n"); return(ret); } UniValue height_MoM(const UniValue& params, bool fHelp, const CPubKey& mypk) { int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); if ( fHelp || params.size() != 1 ) throw runtime_error("height_MoM height\n"); LOCK(cs_main); height = atoi(params[0].get_str().c_str()); if ( height <= 0 ) { if ( chainActive.Tip() == 0 ) { ret.push_back(Pair("error",(char *)"no active chain yet")); return(ret); } height = chainActive.Tip()->GetHeight(); } //fprintf(stderr,"height_MoM height.%d\n",height); depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); ret.push_back(Pair("coin",(char *)(SMART_CHAIN_SYMBOL[0] == 0 ? "HUSH" : SMART_CHAIN_SYMBOL))); ret.push_back(Pair("height",height)); ret.push_back(Pair("timestamp",(uint64_t)timestamp)); if ( depth > 0 ) { ret.push_back(Pair("depth",depth)); ret.push_back(Pair("notarized_height",notarized_height)); ret.push_back(Pair("MoM",MoM.GetHex())); ret.push_back(Pair("kmdtxid",kmdtxid.GetHex())); if ( SMART_CHAIN_SYMBOL[0] != 0 ) { ret.push_back(Pair("MoMoM",MoMoM.GetHex())); ret.push_back(Pair("MoMoMoffset",MoMoMoffset)); ret.push_back(Pair("MoMoMdepth",MoMoMdepth)); ret.push_back(Pair("kmdstarti",kmdstarti)); ret.push_back(Pair("kmdendi",kmdendi)); } } else ret.push_back(Pair("error",(char *)"no MoM for height")); return ret; } UniValue MoMoMdata(const UniValue& params, bool fHelp, const CPubKey& mypk) { if ( fHelp || params.size() != 3 ) throw runtime_error("MoMoMdata symbol kmdheight ccid\n"); UniValue ret(UniValue::VOBJ); char* symbol = (char *)params[0].get_str().c_str(); int kmdheight = atoi(params[1].get_str().c_str()); uint32_t ccid = atoi(params[2].get_str().c_str()); ret.push_back(Pair("coin",symbol)); ret.push_back(Pair("kmdheight",kmdheight-5)); ret.push_back(Pair("ccid", (int) ccid)); uint256 destNotarizationTxid; std::vector moms; uint256 MoMoM = CalculateProofRoot(symbol, ccid, kmdheight-5, moms, destNotarizationTxid); UniValue valMoms(UniValue::VARR); for (int i=0; i= height ) throw runtime_error("calc_MoM illegal height or MoMdepth\n"); //fprintf(stderr,"height_MoM height.%d\n",height); MoM = komodo_calcMoM(height,MoMdepth); ret.push_back(Pair("coin",(char *)(SMART_CHAIN_SYMBOL[0] == 0 ? "HUSH" : SMART_CHAIN_SYMBOL))); ret.push_back(Pair("height",height)); ret.push_back(Pair("MoMdepth",MoMdepth)); ret.push_back(Pair("MoM",MoM.GetHex())); return ret; } UniValue migrate_converttoexport(const UniValue& params, bool fHelp, const CPubKey& mypk) { std::vector rawproof; uint8_t *ptr; uint8_t i; uint32_t ccid = ASSETCHAINS_CC; uint64_t txfee = 10000; if (fHelp || params.size() != 2) throw runtime_error( "migrate_converttoexport rawTx dest_symbol\n" "\nConvert a raw transaction to a cross-chain export.\n" "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 to get the corresponding " "import transaction.\n" ); if (ASSETCHAINS_CC < HUSH_FIRSTFUNGIBLEID) throw runtime_error("-ac_cc < HUSH_FIRSTFUNGIBLEID"); if (SMART_CHAIN_SYMBOL[0] == 0) throw runtime_error("Must be called on assetchain"); vector txData(ParseHexV(params[0], "argument 1")); CMutableTransaction tx; if (!E_UNMARSHAL(txData, ss >> tx)) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); string targetSymbol = params[1].get_str(); if (targetSymbol.size() == 0 || targetSymbol.size() > 32) throw runtime_error("targetSymbol length must be >0 and <=32"); if (strcmp(SMART_CHAIN_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 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(SMART_CHAIN_SYMBOL)); ptr = rawproof.data(); for (i=0; i 32) throw runtime_error("targetSymbol length must be >0 and <=32"); if (strcmp(SMART_CHAIN_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(SMART_CHAIN_SYMBOL); std::vector rawproof; //(chainSymbol.begin(), chainSymbol.end()); if (tokenid.IsNull()) { // coins int64_t inputs; if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 10)) == 0) { throw runtime_error("not enough funds, or need to merge utxos first\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 vorigpubkey, vdestpubkey; std::string name, description; std::vector> 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> 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 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 (!myGetTransaction(burntxid, burnTx, blockHash)) 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 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 voutPubkeys; std::vector> 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 origpubkey; std::string name, description; std::vector> 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 != SMART_CHAIN_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 == SMART_CHAIN_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 from a chain to chain * * 1.Create a transaction on assetchain (deprecated): * 1.1 generaterawtransaction * 1.2 migrate_converttoexport * 1.3 fundrawtransaction * 1.4 signrawtransaction * * alternatively, burn (export) transaction may be created with this new rpc call: * 1. migrate_createburntransaction * * next steps: * 2. migrate_createimporttransaction * 3. migrate_completeimporttransaction */ UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp, const CPubKey& mypk) { 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 < HUSH_FIRSTFUNGIBLEID) throw runtime_error("-ac_cc < HUSH_FIRSTFUNGIBLEID"); if (SMART_CHAIN_SYMBOL[0] == 0) throw runtime_error("Must be called on assetchain"); vector txData(ParseHexV(params[0], "argument 1")); CTransaction burnTx; 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 payouts; if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts)) throw runtime_error("Couldn't parse payouts"); 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 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(importProof, burnTx, payouts); 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, const CPubKey& mypk) { if (fHelp || params.size() < 1 || params.size() > 2) 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 HUSH height, use when import fails."); if (SMART_CHAIN_SYMBOL[0] != 0) throw runtime_error("Must be called on HUSH"); 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); 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, const CPubKey& mypk) { 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 (SMART_CHAIN_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, mypk))); // 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, const CPubKey& mypk) { 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 (SMART_CHAIN_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 proofData = ParseHex(params[1].get_str()); CMerkleBlock merkleBlock; std::vector 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, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); std::string destaddr; std::string source; std::string sourceTxHex; std::string importTxHex; CTransaction burnTx; CTxOut burnOut; uint64_t burnAmount; uint256 sourcetxid, blockHash; std::vector vouts; std::vector 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" //TODO: "or selfimport rawburntx burntxid {nvout|\"find\"} rawproof source bindtxid height} \n" "\ncreates self import coin transaction"); destaddr = params[0].get_str(); burnAmount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param if (source == "BEAM") { if (ASSETCHAINS_BEAMPORT == 0) return(-1); // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn // return(0); return -1; } else if (source == "CODA") { if (ASSETCHAINS_CODAPORT == 0) return(-1); // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn // return(0); return -1; } else if (source == "PEGSCC") { return -1; } else if (source == "PUBKEY") { ImportProof proofNull; CTxDestination dest = DecodeDestination(destaddr.c_str()); 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(sourceMtx, templateMtx, proofNull) < 0) throw std::runtime_error("Failed creating selfimport template tx"); vouts = templateMtx.vout; 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 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) { return -1; } return result; } bool GetNotarizationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarizationNotaries); UniValue importdual(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::string hex,source,sourceaddr,destaddr,burntxid; uint64_t burnAmount; CPubKey destpub; std::vector vouts; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importdual only works on -ac_import chains"); if (fHelp || params.size() < 4) throw runtime_error("burntxid source_addr dest_pubkey amount\n"); CCerror = ""; burntxid = params[0].get_str(); sourceaddr = params[1].get_str(); destaddr = params[2].get_str(); burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param CTxDestination dest = DecodeDestination(destaddr.c_str()); CScript scriptPubKey = GetScriptForDestination(dest); vouts.push_back(CTxOut(burnAmount,scriptPubKey)); if (source == "BEAM") { if (ASSETCHAINS_BEAMPORT == 0) return(-1); // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn // return(0); return -1; } else if (source == "CODA") { if (ASSETCHAINS_CODAPORT == 0) return(-1); hex=MakeCodaImportTx(0,burntxid,sourceaddr,vouts); // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn // return(0); } else if (source == "PEGSCC") { return -1; } RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importdual"); return result; } UniValue importgatewayinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 txid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaybind only works on -ac_import chains"); if ( fHelp || params.size() != 1 ) throw runtime_error("importgatewayinfo bindtxid\n"); txid = Parseuint256(params[0].get_str().c_str()); return(ImportGatewayInfo(txid)); } UniValue importgatewaybind(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector pubkey; std::string hex,coin; int32_t i,M,N; std::vector pubkeys; uint256 oracletxid; uint8_t p1,p2,p3,p4; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaybind only works on -ac_import chains"); if ( fHelp || params.size() != 8) throw runtime_error("use \'importgatewaybind coin orcletxid M N pubkeys pubtype p2shtype wiftype [taddr]\' to bind an import gateway\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; coin = params[0].get_str(); oracletxid = Parseuint256(params[1].get_str().c_str()); M = atoi(params[2].get_str().c_str()); N = atoi(params[3].get_str().c_str()); if ( M > N || N == 0 || N > 15 ) throw runtime_error("illegal M or N > 15\n"); if ( params.size() < 4+N+3 ) throw runtime_error("not enough parameters for N pubkeys\n"); for (i=0; i 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importgatewaybind"); return result; } UniValue importgatewaydeposit(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector rawproof; std::string hex,coin,rawburntx; int32_t height,burnvout; int64_t amount; CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaydeposit only works on -ac_import chains"); if ( fHelp || params.size() != 9) throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub amount\' to import deposited coins\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; bindtxid = Parseuint256(params[0].get_str().c_str()); height = atoi(params[1].get_str().c_str()); coin = params[2].get_str(); burntxid = Parseuint256(params[3].get_str().c_str()); burnvout = atoi(params[4].get_str().c_str()); rawburntx = params[5].get_str(); rawproof = ParseHex(params[6].get_str()); destpub = ParseHex(params[7].get_str()); amount = atof(params[8].get_str().c_str()) * COIN + 0.00000000499999; if (coin == "BEAM" || coin == "CODA") { ERR_RESULT("for BEAM and CODA import use importdual RPC"); return result; } else if (coin != ASSETCHAINS_SELFIMPORT) { ERR_RESULT("source coin not equal to ac_import name"); return result; } hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub, amount); RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importgatewaydeposit"); return result; } UniValue importgatewaywithdraw(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx; std::vector rawproof; std::string hex,coin,rawburntx; int64_t amount; int32_t height,burnvout; CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaywithdraw only works on -ac_import chains"); if ( fHelp || params.size() != 4) throw runtime_error("use \'importgatewaywithdraw bindtxid coin withdrawpub amount\' to burn imported coins and withdraw them on external chain\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); CCerror = ""; bindtxid = Parseuint256(params[0].get_str().c_str()); coin = params[1].get_str(); destpub = ParseHex(params[2].get_str()); amount = atof((char *)params[3].get_str().c_str()) * COIN + 0.00000000499999; if (coin == "BEAM" || coin == "CODA") { ERR_RESULT("for BEAM and CODA import use importdual RPC"); return result; } else if (coin != ASSETCHAINS_SELFIMPORT) { ERR_RESULT("source coin not equal to ac_import name"); return result; } hex = ImportGatewayWithdraw(0, bindtxid, coin, destpub, amount); RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importgatewaywithdraw"); return result; } UniValue importgatewaypartialsign(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewayspartialsign only works on -ac_import chains"); if ( fHelp || params.size() != 3 ) throw runtime_error("importgatewayspartialsign txidaddr refcoin hex\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); txid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); parthex = params[2].get_str(); hex = ImportGatewayPartialSign(0,txid,coin,parthex); RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex",hex)); } else ERR_RESULT("couldnt importgatewayspartialsign"); return(result); } UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,hex,coin; if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("importgatewaycompletesigning only works on -ac_import chains"); if ( fHelp || params.size() != 3 ) throw runtime_error("importgatewaycompletesigning withdrawtxid coin hex\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); txhex = params[2].get_str(); hex = ImportGatewayCompleteSigning(0,withdrawtxid,coin,txhex); RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importgatewaycompletesigning"); return(result); } UniValue importgatewaymarkdone(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); uint256 completetxid; std::string hex,coin; if ( fHelp || params.size() != 2 ) throw runtime_error("importgatewaymarkdone completesigningtx coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); completetxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); hex = ImportGatewayMarkDone(0,completetxid,coin); RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); } else ERR_RESULT("couldnt importgatewaymarkdone"); return(result); } UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) throw runtime_error("importgatewaypendingwithdraws bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); return(ImportGatewayPendingWithdraws(bindtxid,coin)); } UniValue importgatewayprocessed(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) throw runtime_error("importgatewayprocessed bindtxid coin\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); return(ImportGatewayProcessedWithdraws(bindtxid,coin)); } UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; CPubKey pubkey; if ( fHelp || params.size() != 2) throw runtime_error("importgatewayexternaladdress bindtxid pubkey\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); pubkey = ParseHex(params[1].get_str().c_str()); return(ImportGatewayExternalAddress(bindtxid,pubkey)); } UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 bindtxid; if ( fHelp || params.size() != 2) throw runtime_error("importgatewaydumpprivkey bindtxid address\n"); if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) throw runtime_error(CC_REQUIREMENTS_MSG); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); std::string strAddress = params[1].get_str(); CTxDestination dest = DecodeDestination(strAddress); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transparent address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); } CKey vchSecret; // if (!pwalletMain->GetKey(*keyID, vchSecret)) { // throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); //} return(ImportGatewayDumpPrivKey(bindtxid,vchSecret)); } UniValue getNotarizationsForBlock(const UniValue& params, bool fHelp, const CPubKey& mypk) { // TODO take timestamp as param, and loop blockindex to get starting/finish height. if (fHelp || params.size() != 1) throw runtime_error("getNotarizationsForBlock height\n\n" "Takes a block height and returns notarisation information " "within the block"); LOCK(cs_main); int32_t height = params[0].get_int(); if ( height < 0 || height > chainActive.Height() ) throw runtime_error("height out of range.\n"); uint256 blockHash = chainActive[height]->GetBlockHash(); NotarizationsInBlock nibs; GetBlockNotarizations(blockHash, nibs); UniValue out(UniValue::VOBJ); //out.push_back(make_pair("blocktime",(int))); UniValue hush(UniValue::VARR); int8_t numNN = 0; uint8_t notarypubkeys[64][33] = {0}; numNN = hush_notaries(notarypubkeys, height, chainActive[height]->nTime); BOOST_FOREACH(const Notarization& n, nibs) { UniValue item(UniValue::VOBJ); UniValue notaryarr(UniValue::VARR); std::vector NotarizationNotaries; uint256 hash; CTransaction tx; if ( myGetTransaction(n.first,tx,hash) ) { if ( !GetNotarizationNotaries(notarypubkeys, numNN, tx.vin, NotarizationNotaries) ) continue; } item.push_back(make_pair("txid", n.first.GetHex())); item.push_back(make_pair("chain", n.second.symbol)); item.push_back(make_pair("height", (int)n.second.height)); item.push_back(make_pair("blockhash", n.second.blockHash.GetHex())); //item.push_back(make_pair("HUSH_height", height)); // for when timstamp input is used. for ( auto notary : NotarizationNotaries ) notaryarr.push_back(notary); item.push_back(make_pair("notaries",notaryarr)); hush.push_back(item); } out.push_back(make_pair("HUSH", hush)); return out; } /*UniValue getNotarizationsForBlock(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() != 1) throw runtime_error("getNotarizationsForBlock blockHash\n\n" "Takes a block hash and returns notarisation transactions " "within the block"); uint256 blockHash = uint256S(params[0].get_str()); NotarizationsInBlock nibs; GetBlockNotarizations(blockHash, nibs); UniValue out(UniValue::VARR); BOOST_FOREACH(const Notarization& n, nibs) { UniValue item(UniValue::VARR); item.push_back(n.first.GetHex()); item.push_back(HexStr(E_MARSHAL(ss << n.second))); out.push_back(item); } return out; }*/ UniValue scanNotarizationsDB(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() < 2 || params.size() > 3) throw runtime_error("scanNotarizationsDB blockHeight symbol [blocksLimit=1440]\n\n" "Scans notarisationsdb backwards from height for a notarisation" " of given symbol"); int height = atoi(params[0].get_str().c_str()); std::string symbol = params[1].get_str().c_str(); int limit = 1440; if (params.size() > 2) { limit = atoi(params[2].get_str().c_str()); } if (height == 0) { height = chainActive.Height(); } Notarization nota; int matchedHeight = ScanNotarizationsDB(height, symbol, limit, nota); if (!matchedHeight) return NullUniValue; UniValue out(UniValue::VOBJ); out.pushKV("height", matchedHeight); out.pushKV("hash", nota.first.GetHex()); out.pushKV("opreturn", HexStr(E_MARSHAL(ss << nota.second))); return out; } UniValue getimports(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() != 1) throw runtime_error( "getimports \"hash|height\"\n" "\n\n" "\nResult:\n" "{\n" " \"imports\" : [ (json array)\n" " \"transactionid\" : { (json object)\n" " \"value\" : (numeric)\n" " \"address\" : (string)\n" " \"export\" { (json object)\n" " \"txid\" : (string)\n" " \"value\" : (numeric)\n" " \"chain\" : (string)\n" " }\n" " }" " ]\n" " \"TotalImported\" : (numeric)\n" " \"time\" : (numeric)\n" "}\n" "\nExamples:\n" + HelpExampleCli("getimports", "\"00000000febc373a1da2bd9f887b105ad79ddc26ac26c2b28652d64e5207c5b5\"") + HelpExampleRpc("getimports", "\"00000000febc373a1da2bd9f887b105ad79ddc26ac26c2b28652d64e5207c5b5\"") + HelpExampleCli("getimports", "12800") + HelpExampleRpc("getimports", "12800") ); LOCK(cs_main); std::string strHash = params[0].get_str(); // If height is supplied, find the hash if (strHash.size() < (2 * sizeof(uint256))) { // std::stoi allows characters, whereas we want to be strict regex r("[[:digit:]]+"); if (!regex_match(strHash, r)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); } int nHeight = -1; try { nHeight = std::stoi(strHash); } catch (const std::exception &e) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); } if (nHeight < 0 || nHeight > chainActive.Height()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); } strHash = chainActive[nHeight]->GetBlockHash().GetHex(); } uint256 hash(uint256S(strHash)); if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); if(!ReadBlockFromDisk(block, pblockindex,1)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); UniValue result(UniValue::VOBJ); CAmount TotalImported = 0; UniValue imports(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) { if(tx.IsCoinImport()) { UniValue objTx(UniValue::VOBJ); objTx.push_back(Pair("txid",tx.GetHash().ToString())); ImportProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; TotalImported += tx.vout[0].nValue; // were vouts swapped? 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); CPubKey vinPubkey; if (UnmarshalImportTx(tx, proof, burnTx, payouts)) { if (burnTx.vout.size() == 0) continue; objBurnTx.push_back(Pair("txid", burnTx.GetHash().ToString())); objBurnTx.push_back(Pair("amount", ValueFromAmount(burnTx.vout.back().nValue))); // extract op_return to get burn source chain. std::vector burnOpret; std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; std::vectorrawproof; if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) { if (rawproof.size() > 0) { 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())); } } } objTx.push_back(Pair("export", objBurnTx)); imports.push_back(objTx); } } result.push_back(Pair("imports", imports)); result.push_back(Pair("TotalImported", TotalImported > 0 ? ValueFromAmount(TotalImported) : 0 )); result.push_back(Pair("time", block.GetBlockTime())); return result; } // outputs burn transactions in the wallet UniValue getwalletburntransactions(const UniValue& params, bool fHelp, const CPubKey& mypk) { 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 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 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> oprets; uint256 tokenid; uint8_t evalCodeInOpret; std::vector 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 vorigpubkey; std::string name, description; std::vector> 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 // check for corrupted strings (look for non-printable chars) from some older versions // which caused "couldn't parse reply from server" error on client: if (std::find_if(targetSymbol.begin(), targetSymbol.end(), [](int c) {return !std::isprint(c);}) != targetSymbol.end()) targetSymbol = ""; 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 arrTmp = ret.getValues(); vector::iterator first = arrTmp.begin(); vector::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; }