@ -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 "
" \n Creates 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 ( im portP roof, 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"
" \n creates 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 , rawp roofEmpty ) ;
burnOut = MakeBurnOutput ( burnAmount , 0xffffffff , ASSETCHAINS_SELFIMPORT , vouts , rawP roofEmpty ) ;
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 ( ) ) ) ;
Tx Proof 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 ) )
Import Proof 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 "
" \n Result: \n "
" [ \n "
" { \n "
" \" txid \" : (string) \n "
" \" burnedAmount \" : (numeric) \n "
" \" targetSymbol \" : (string) \n "
" \" targetCCid \" : (numeric) \n "
" } \n "
" ] \n "
" \n Examples: \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 ;
}