From 1f013df15670f8f96790858d9418136ac9350ab2 Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Thu, 4 Jul 2019 11:23:55 +0200 Subject: [PATCH] Initial PegsCC implementation (#27) --- src/cc/CCPegs.h | 11 +- src/cc/CCinclude.h | 5 +- src/cc/CCtokens.cpp | 15 + src/cc/CCtx.cpp | 5 +- src/cc/CCutils.cpp | 5 +- src/cc/channels.cpp | 2 +- src/cc/heir_validate.h | 5 +- src/cc/import.cpp | 13 +- src/cc/pegs.cpp | 1228 ++++++++++++++++++++++++++++++++-- src/coins.cpp | 8 +- src/importcoin.cpp | 57 +- src/importcoin.h | 4 + src/komodo_globals.h | 2 +- src/komodo_utils.h | 40 +- src/main.cpp | 29 +- src/miner.cpp | 7 + src/primitives/transaction.h | 6 + src/rpc/rawtransaction.cpp | 2 +- src/rpc/server.cpp | 12 + src/rpc/server.h | 10 + src/txmempool.cpp | 5 + src/wallet/rpcwallet.cpp | 199 ++++++ 22 files changed, 1581 insertions(+), 89 deletions(-) diff --git a/src/cc/CCPegs.h b/src/cc/CCPegs.h index 8d2a268f1..e4d390d0e 100644 --- a/src/cc/CCPegs.h +++ b/src/cc/CCPegs.h @@ -22,6 +22,15 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PegsInfo(); +std::string PegsCreate(uint64_t txfee,int64_t amount,std::vector bindtxids); +std::string PegsFund(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +std::string PegsGet(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +std::string PegsRedeem(uint64_t txfee,uint256 pegstxid, uint256 tokenid); +std::string PegsLiquidate(uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid); +std::string PegsExchange(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount); +UniValue PegsAccountHistory(uint256 pegstxid); +UniValue PegsAccountInfo(uint256 pegstxid); +UniValue PegsWorstAccounts(uint256 pegstxid); +UniValue PegsInfo(uint256 pegstxid); #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 1d8cf669e..2df5bc39f 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -79,6 +79,7 @@ one other technical note is that komodod has the insight-explorer extensions bui OPRETID_CHANNELSDATA = 0x14, OPRETID_HEIRDATA = 0x15, OPRETID_ROGUEGAMEDATA = 0x16, + OPRETID_PEGSDATA = 0x17, // non cc contract data: OPRETID_FIRSTNONCCDATA = 0x80, @@ -126,7 +127,7 @@ struct CCcontract_info // the same for tokens 1of2 keys cc char tokens1of2addr[64]; - CPubKey tokens1of2pk[2]; + CPubKey tokens1of2pk[2]; uint8_t tokens1of2priv[32]; // this is for spending from two additional 'unspendable' CC addresses of other eval codes // (that is, for spending from several cc contract 'unspendable' addresses): @@ -249,7 +250,7 @@ CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr); int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 5f5e56ebb..c664fd49d 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -397,6 +397,21 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true } } + //special check for 3 pubkey tx when spending from 1of2 CC address with on of pubkeys is global CC pubkey + struct CCcontract_info *cpEvalCode1,CEvalCode1; + cpEvalCode1 = CCinit(&CEvalCode1,evalCode1); + CPubKey pk=GetUnspendable(cpEvalCode1,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval1 pegscc cc1of2 pk[0]")) ); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval1 pegscc cc1of2 pk[1]")) ); + if (evalCode2!=0) + { + struct CCcontract_info *cpEvalCode2,CEvalCode2; + cpEvalCode2 = CCinit(&CEvalCode2,evalCode2); + CPubKey pk=GetUnspendable(cpEvalCode2,0); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0], pk), std::string("dual-eval2 pegscc cc1of2 pk[0]")) ); + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1], pk), std::string("dual-eval2 pegscc cc1of2 pk[1]")) ); + } + // maybe it is single-eval or dual/three-eval token change? std::vector vinPubkeys, vinPubkeysUnfiltered; ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 7435defbf..4a9eef911 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -96,6 +96,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; itokens1of2addr, destaddr) == 0) { //fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); - privkey = myprivkey; + privkey = cp->tokens1of2priv;//myprivkey; if (othercond1of2tokens == 0) // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc // TODO: verify evalcodes order if additionalEvalcode2 is not 0 diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 5de5ed0db..7d94a37a9 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -187,7 +187,7 @@ void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t * } // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 cryptocondition vout: -void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t *priv,char *coinaddr) +void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr) { cp->coins1of2pk[0] = pk1; cp->coins1of2pk[1] = pk2; @@ -197,10 +197,11 @@ void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 token cryptocondition vout // to get tokenaddr use GetTokensCCaddress() -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *tokenaddr) +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *tokenaddr) { cp->tokens1of2pk[0] = pk1; cp->tokens1of2pk[1] = pk2; + memcpy(cp->tokens1of2priv,priv,32); strcpy(cp->tokens1of2addr, tokenaddr); } diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 64bb95572..2d161675c 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -451,7 +451,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C mtx.vin.push_back(CTxIn(txid,0,CScript())); mtx.vin.push_back(CTxIn(txid,marker,CScript())); Myprivkey(myprivkey); - if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,coinaddr); + if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,myprivkey,coinaddr); else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr); return totalinputs; } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index a27dc3340..c7424bc16 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -98,8 +98,9 @@ public: } static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, mypriv, coinaddr); } }; diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 35b4f5405..aa2ea5d11 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -681,7 +681,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp return Invalid("invalid-params"); // Control all aspects of this transaction // It should not be at all malleable - if (MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication + if (ASSETCHAINS_SELFIMPORT!="PEGSCC" && MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication return Invalid("non-canonical"); // burn params if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof)) @@ -736,10 +736,17 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 ) return Invalid("CODA-import-failure"); } + else if ( targetSymbol == "PEGSCC" ) + { + if ( ASSETCHAINS_SELFIMPORT != "PEGSCC" ) + return Invalid("PEGSCC-import-when-not PEGSCC"); + // else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 ) + // return Invalid("PEGSCC-import-failure"); + } else if ( targetSymbol == "PUBKEY" ) { if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" ) - return Invalid("PUBKEY-import-when-notPUBKEY"); + return Invalid("PUBKEY-import-when-not PUBKEY"); else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 ) return Invalid("PUBKEY-import-failure"); } @@ -747,7 +754,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp { if ( targetSymbol != ASSETCHAINS_SELFIMPORT ) return Invalid("invalid-gateway-import-coin"); - else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 ) + else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 ) return Invalid("GATEWAY-import-failure"); } } diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 21cee8ca1..d76a9c6a9 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -14,6 +14,8 @@ ******************************************************************************/ #include "CCPegs.h" +#include "../importcoin.h" +#include "key_io.h" /* pegs CC is able to create a coin backed (by any supported coin with gateways CC deposits) and pegged to any synthetic price that is able to be calculated based on prices CC @@ -84,6 +86,197 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC */ // start of consensus code +#ifndef PEGS_TRSHOLDS +#define PEGS_TRSHOLDS +#define PEGS_ACCOUNT_YELLOW_ZONE 60 +#define PEGS_ACCOUNT_TRESHOLD 90 +#define PEGS_GLOBAL_TRESHOLD 60 +#endif // PEGS_TRSHOLDS +#define CC_MARKER_VALUE 10000 + +extern uint64_t ASSETCHAINS_PEGSCCPARAMS[3]; + +extern uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); +extern int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); +extern int32_t komodo_currentheight(); + +CScript EncodePegsCreateOpRet(std::vector bindtxids) +{ + CScript opret; uint8_t evalcode = EVAL_PEGS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << bindtxids); + return(opret); +} + +uint8_t DecodePegsCreateOpRet(const CScript &scriptPubKey,std::vector &bindtxids) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bindtxids) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsFundOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='F'; struct CCcontract_info *cp,C; CPubKey pegspk; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + pubkeys.push_back(srcpub); + pubkeys.push_back(pegspk); + LOGSTREAM("pegscc", CCLOG_DEBUG1, stream << "EncodePegsFundOpRet [" << account.first << "," << account.second << "]" << std::endl); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsFundOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodePegsGetOpRet(const CTransaction tx,uint256& pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector vopret; uint8_t *script; + ImportProof proof; CTransaction burntx; std::vector payouts; + + GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret); + + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_IMPORTCOIN && UnmarshalImportTx(tx,proof,burntx,payouts) && UnmarshalBurnTx(burntx,pegstxid,tokenid,srcpub,amount,account)) + { + return('G'); + } + return(0); +} + +CScript EncodePegsReedemOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='R'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsRedeemOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsExchangeOpRet(uint256 tokenid,uint256 pegstxid,CPubKey pk1,CPubKey pk2,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='E'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; CPubKey pegspk; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + pubkeys.push_back(pk1); + pubkeys.push_back(pk2); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << pk1 << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsExchangeOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodePegsLiquidateOpRet(uint256 tokenid,uint256 pegstxid,CPubKey srcpub,int64_t amount,std::pair account) +{ + CScript opret; uint8_t evalcode=EVAL_PEGS,funcid='L'; struct CCcontract_info *cp,C; + std::vector pubkeys; vscript_t vopret; + + cp = CCinit(&C,EVAL_PEGS); + pubkeys.push_back(srcpub); + vopret = E_MARSHAL(ss << evalcode << funcid << pegstxid << srcpub << amount << account); + return(EncodeTokenOpRet(tokenid,pubkeys,make_pair(OPRETID_PEGSDATA, vopret))); +} + +uint8_t DecodePegsLiquidateOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &pegstxid,CPubKey &srcpub,int64_t &amount,std::pair &account) +{ + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_PEGS && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid; ss >> srcpub; ss >> amount; ss >> account) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodePegsOpRet(CTransaction tx,uint256& pegstxid,uint256& tokenid) +{ + std::vector> oprets; int32_t numvouts=tx.vout.size(); + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + ImportProof proof; CTransaction burntx; std::vector payouts; uint256 tmppegstxid; CPubKey srcpub; int64_t amount; std::pair account; + + if (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_PEGSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(tx.vout[numvouts-1].scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if (tx.IsPegsImport()) + return(DecodePegsGetOpRet(tx,pegstxid,tokenid,srcpub,amount,account)); + else if ( vopret.size() > 2 && script[0] == EVAL_PEGS) + { + E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pegstxid); + return(f); + } + return(0); +} int64_t IsPegsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { @@ -137,7 +330,7 @@ bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - return eval->Invalid("no validation yet"); + return (true); std::vector > txids; numvins = tx.vin.size(); numvouts = tx.vout.size(); @@ -175,12 +368,14 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk1,CPubKey pk2,int64_t total,int32_t maxinputs) { // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); + + if (pk2.IsValid()) GetCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetCCaddress(cp,coinaddr,pk1); SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { @@ -189,7 +384,7 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsPegsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -204,76 +399,1005 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe return(totalinputs); } -std::string PegsGet(uint64_t txfee,int64_t nValue) +int64_t AddPegsTokenInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint256 pegstxid, uint256 tokenid, CPubKey pk1,CPubKey pk2, int64_t total,int32_t maxinputs) { - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Pegspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; - cp = CCinit(&C,EVAL_PEGS); - if ( txfee == 0 ) - txfee = 10000; - Pegspk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddPegsInputs(cp,mtx,Pegspk,nValue+txfee,60)) > 0 ) + // add threshold check + char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; uint256 tmppegstxid,tmptokenid; CPubKey mypk; + + if (pk2.IsValid()) GetTokensCCaddress1of2(cp,coinaddr,pk1,pk2); + else GetTokensCCaddress(cp,coinaddr,pk1); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,CCchange,Pegspk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); - j = rand() & 0xfffffff; - for (i=0; i<1000000; i++,j++) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // no need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PEGS << (uint8_t)'G' << j)); - if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 && DecodePegsOpRet(vintx,tmppegstxid,tmptokenid)!=0 && tmppegstxid==pegstxid && tmptokenid==tokenid) { - len >>= 1; - decode_hex(buf,len,(char *)rawhex.c_str()); - hash = bits256_doublesha256(0,buf,len); - if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) - { - fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); - return(rawhex); - } - //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; } } - fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); - return(""); - } else fprintf(stderr,"cant find Pegs inputs\n"); + } + if (pk2.IsValid()) + { + mypk = pubkey2pk(Mypubkey()); + if (mypk!=pk1 && mypk!=pk2) + { + CCaddrTokens1of2set(cp,pk1,pk2,cp->CCpriv,coinaddr); + } + else + { + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddrTokens1of2set(cp,pk1,pk2,mypriv,coinaddr); + } + } + return(totalinputs); +} + +std::string PegsDecodeAccountTx(CTransaction tx,CPubKey& pk,int64_t &amount,std::pair &account) +{ + uint256 hashBlock,tokenid,pegstxid; int32_t numvouts=tx.vout.size(); char funcid; + + if ((funcid=DecodePegsOpRet(tx,pegstxid,tokenid))!=0) + { + switch(funcid) + { + case 'F': if (DecodePegsFundOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='F') return("fund"); + break; + case 'G': if (DecodePegsGetOpRet(tx,pegstxid,tokenid,pk,amount,account)=='G') return("get"); + break; + case 'R': if (DecodePegsRedeemOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("redeem"); + break; + case 'E': if (DecodePegsExchangeOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='R') return("exchange"); + break; + case 'L': if (DecodePegsLiquidateOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,pegstxid,pk,amount,account)=='L') return("liquidate"); + break; + } + } + return (""); +} + +char PegsFindAccount(struct CCcontract_info *cp,CPubKey pk,uint256 pegstxid, uint256 tokenid, uint256 &accounttxid, std::pair &account) +{ + char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx,acctx; int32_t numvouts,vout,ratio; char funcid,f; CPubKey pegspk,tmppk; + std::vector > unspentOutputs; + ImportProof proof; CTransaction burntx; std::vector payouts; + + accounttxid=zeroid; + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 1 && nValue == CC_MARKER_VALUE && myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size())>0 && + (f=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + accounttxid=txid; + funcid=f; + acctx=tx; + } + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + accounttxid=zeroid; + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && (f=DecodePegsOpRet(txmempool,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + funcid=f; + accounttxid=hash; + acctx=txmempool; + } + } + } + if (accounttxid!=zeroid) + { + PegsDecodeAccountTx(acctx,tmppk,tmpamount,account); + return(funcid); + } + else return(0); +} + +double PegsGetTokenPrice(uint256 tokenid) +{ + int64_t *tokensyn,*btcusd; double price; CTransaction tokentx; uint256 hashBlock; + std::string name,desc; std::vector vorigpubkey; int32_t numvouts; + + if (GetTransaction(tokenid,tokentx,hashBlock,false)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + { + tokensyn = (int64_t *)calloc(sizeof(*tokensyn) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); + btcusd = (int64_t *)calloc(sizeof(*btcusd) * 3, 1 + PRICES_DAYWINDOW * 2 + PRICES_SMOOTHWIDTH); + if (komodo_priceget(tokensyn, komodo_priceind((name+"_BTC").c_str()), komodo_currentheight(), 1) >= 0 && komodo_priceget(btcusd, komodo_priceind("BTC_USD"), komodo_currentheight(), 1) >= 0) + { + price=tokensyn[2]*btcusd[2]; + price=price/COIN/COIN; + return (price); + } + } + return (0); +} + +std::string PegsGetTokenName(uint256 tokenid) +{ + CTransaction tokentx; uint256 hashBlock; std::string name,desc; std::vector vorigpubkey; int32_t numvouts; + + if (GetTransaction(tokenid,tokentx,hashBlock,false)!=0 && (numvouts=tokentx.vout.size())>0 && DecodeTokenCreateOpRet(tokentx.vout[numvouts-1].scriptPubKey,vorigpubkey,name,desc)=='c') + { + return (name); + } + CCerror = strprintf("cant find token create or invalid tokenid %s",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } -std::string PegsFund(uint64_t txfee,int64_t funds) +double PegsGetAccountRatio(uint256 pegstxid,uint256 tokenid,uint256 accounttxid) +{ + int64_t amount; uint256 hashBlock,tmptokenid,tmppegstxid; + CTransaction tx; int32_t numvouts; char funcid; CPubKey pk; + std::pair account; struct CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_PEGS); + if (GetTransaction(accounttxid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid && tokenid==tmptokenid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + return ((double)account.second*100/(account.first*PegsGetTokenPrice(tokenid))); + } + return (0); +} + +double PegsGetGlobalRatio(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount,globaldebt=0; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + std::map> globalaccounts; double globaldeposit=0; + struct CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_PEGS); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid && (funcid=='F' || funcid=='G' || funcid=='E')) + { + PegsDecodeAccountTx(tx,pk,amount,account); + globalaccounts[tokenid].first+=account.first; + globalaccounts[tokenid].second+=account.second; + } + } + unspentOutputs.clear(); + GetTokensCCaddress(cp,coinaddr,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && DecodePegsOpRet(tx,tmppegstxid,tokenid)!=0 && pegstxid==tmppegstxid) + { + globalaccounts[tokenid].first+=nValue; + } + } + for (std::map>::iterator it = globalaccounts.begin(); it != globalaccounts.end(); ++it) + { + globaldeposit+=globalaccounts[it->first].first*PegsGetTokenPrice(it->first); + globaldebt+=globalaccounts[it->first].second; + } + if (globaldebt>0) return ((double)globaldebt*100/globaldeposit); + return (0); +} + +std::string PegsFindBestAccount(struct CCcontract_info *cp,uint256 pegstxid, uint256 tokenid, int64_t tokenamount,uint256 &accounttxid, std::pair &account) +{ + char coinaddr[64]; int64_t nValue,tmpamount; uint256 txid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx,acctx; int32_t numvouts,vout; char funcid,f; CPubKey pegspk,tmppk; + std::vector > unspentOutputs; + ImportProof proof; CTransaction burntx; std::vector payouts; double ratio,maxratio=0; + std::pair tmpaccount; + + accounttxid=zeroid; + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 0 && nValue == CC_MARKER_VALUE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) == 0 && + (ratio=PegsGetAccountRatio(pegstxid,tokenid,txid))>(ASSETCHAINS_PEGSCCPARAMS[2]?ASSETCHAINS_PEGSCCPARAMS[2]:PEGS_ACCOUNT_YELLOW_ZONE) && ratio>maxratio) + { + if (myGetTransaction(txid,tx,hashBlock)!=0 && !PegsDecodeAccountTx(tx,tmppk,tmpamount,tmpaccount).empty() && tmpaccount.first>=tokenamount) + { + accounttxid=txid; + acctx=tx; + maxratio=ratio; + } + } + } + if (accounttxid!=zeroid) + { + return(PegsDecodeAccountTx(acctx,tmppk,tmpamount,account)); + } + else return(""); +} + +std::string PegsCreate(uint64_t txfee,int64_t amount, std::vector bindtxids) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Pegspk; CScript opret; struct CCcontract_info *cp,C; + CPubKey mypk,pegspk; struct CCcontract_info *cp,C; CTransaction tx; int32_t numvouts; int64_t totalsupply; std::string coin; + char depositaddr[64]; uint256 txid,hashBlock,tmptokenid,oracletxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_PEGS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - Pegspk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) + pegspk = GetUnspendable(cp,0); + for(auto txid : bindtxids) { - mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds,Pegspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } } + if ( AddNormalinputs(mtx,mypk,amount,64) >= amount ) + { + for (int i=0; i<(amount-txfee)/CC_MARKER_VALUE; i++) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsCreateOpRet(bindtxids))); + } + CCerror = strprintf("error adding normal inputs"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } -UniValue PegsInfo() +std::string PegsFund(uint64_t txfee,uint256 pegstxid, uint256 tokenid,int64_t amount) { - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey Pegspk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Pegs")); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,balance=0,funds=0,tokenfunds=0; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + cp = CCinit(&C,EVAL_PEGS); - Pegspk = GetUnspendable(cp,0); - funding = AddPegsInputs(cp,mtx,Pegspk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ((balance=GetTokenBalance(mypk,tokenid))>=amount) + { + PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account); + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && (funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + funds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + Myprivkey(mypriv); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + } + else funds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee+2*CC_MARKER_VALUE,3); + if (funds>=txfee+2*CC_MARKER_VALUE) + { + if ((tokenfunds=AddTokenCCInputs(cpTokens,mtx,mypk,tokenid,amount,64))>=amount) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,amount,mypk,pegspk)); + if (tokenfunds-amount>0) mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,tokenfunds-amount,mypk)); + if (funds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,funds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first+=amount; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsFundOpRet(tokenid,pegstxid,mypk,amount,account))); + } + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + } + CCerror = strprintf("not enough balance (%lld) for this amount of tokens %lld",balance,amount); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string PegsGet(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) +{ + CMutableTransaction burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()),mtx; + CTransaction pegstx,tx; int32_t numvouts; int64_t funds=0; uint256 accounttxid=zeroid,hashBlock,pricestxid; char coinaddr[64]; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,C; std::pair account(0,0); uint8_t mypriv[32]; + std::vector dummyproof; std::vector vouts; std::vector bindtxids; CScript opret; + + cp = CCinit(&C,EVAL_PEGS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) + { + CCerror = strprintf("cannot find account from which to issue coins, fund account first with pegsfund!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + // spending markers + vouts.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + vouts.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + // coin issue + vouts.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + account.second+=amount; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + // burn tx does not exist in pegs method but it must be created in order for import validation to pass + // fictive burntx input of previous account state tx + burntx.vin.push_back(CTxIn(accounttxid,0,CScript())); + // fictive output of coins in burn tx + burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,"PEGSCC",vouts,dummyproof,pegstxid,tokenid,mypk,amount,account)); + std::vector leaftxids; + BitcoinGetProofMerkleRoot(dummyproof, leaftxids); + MerkleBranch newBranch(0, leaftxids); + TxProof txProof = std::make_pair(burntx.GetHash(), newBranch); + mtx=MakePegsImportCoinTransaction(txProof,burntx,vouts); + Myprivkey(mypriv); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); +} + +std::string PegsRedeem(uint64_t txfee,uint256 pegstxid, uint256 tokenid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)==0) + { + CCerror = strprintf("cannot find account from which to redeem tokens!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + if ((funds=AddNormalinputs(mtx,mypk,account.second,64))>=account.second ) + { + if (accounttxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + pegsfunds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + Myprivkey(mypriv); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + CCaddr1of2set(cp,mypk,pegspk,mypriv,coinaddr); + amount=account.first; + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,mypk,pegspk,amount,64))>=amount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,mypk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,amount,mypk)); + mtx.vout.push_back(CTxOut(account.second,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsReedemOpRet(tokenid,pegstxid,mypk,amount,account))); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough tokens in pegs account (%lld) to redeem this amount of tokens %lld",tokenfunds,account.first); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("to redeem from account and close it you must redeem full debt ammount %lld instead of %lld",account.second,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + + +std::string PegsExchange(uint64_t txfee,uint256 pegstxid, uint256 tokenid, int64_t amount) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,tokenamount,tmpamount; uint256 accounttxid=zeroid,hashBlock,txid,tmptokenid,oracletxid; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype,mypriv[32]; std::vector pubkeys; bool found=false; std::vector bindtxids; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,account)!=0) + { + CCerror = strprintf("you have active account, please close account first before exchanging other coins!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ((funds=AddNormalinputs(mtx,mypk,amount,64))>=amount ) + { + if ((pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + tokenamount=amount/PegsGetTokenPrice(tokenid); + tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,pegspk,CPubKey(),tokenamount,64); + if (tokenfundsCCpriv,coinaddr); + pegsfunds+=2*CC_MARKER_VALUE; + } + if (tokenfunds>=tokenamount) + { + if (accounttxid!=zeroid) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); + } + if ((accounttxid!=zeroid && pegsfunds>=txfee+2*CC_MARKER_VALUE) || pegsfunds>=txfee) + { + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,tokenamount,mypk)); + mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (tokenfunds>tokenamount) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_PEGS,tokenfunds-tokenamount,tmppk,pegspk)); + if (accounttxid!=zeroid) + { + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=account.first-tokenamount; + account.second=account.second-amount; + } + else if (pegsfunds>txfee) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-txfee,pegspk)); + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "modified account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsExchangeOpRet(tokenid,pegstxid,mypk,tmppk,amount,account))); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough tokens in pegs account (%lld) to exchange to this amount of tokens %lld",tokenfunds,tokenamount); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else + { + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + CCerror = strprintf("not enough funds to exchange %lld coins to tokens - balance %lld",amount,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string PegsLiquidate(uint64_t txfee,uint256 pegstxid, uint256 tokenid, uint256 liquidatetxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string coin; + CTransaction pegstx,tx; int32_t numvouts; int64_t totalsupply,pegsfunds=0,funds=0,tokenfunds=0,amount,burnamount; + CPubKey mypk,pegspk,tmppk; struct CCcontract_info *cp,*cpTokens,CTokens,C; char depositaddr[64],coinaddr[64]; std::pair account(0,0),myaccount(0,0); + uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; bool found=false; std::vector bindtxids; + uint256 hashBlock,txid,tmptokenid,oracletxid,accounttxid; + + cp = CCinit(&C,EVAL_PEGS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + if (GetTransaction(pegstxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find pegstxid %s",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodePegsCreateOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxids)!='C') + { + CCerror = strprintf("invalid pegstxid ",pegstxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for(auto txid : bindtxids) + { + if (GetTransaction(txid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("cant find bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmptokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype)!='B') + { + CCerror = strprintf("invalid bindtxid %s",txid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (tmptokenid==tokenid) + { + found=true; + break; + } + } + if (!found) + { + CCerror = strprintf("invalid tokenid ",tokenid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsFindAccount(cp,mypk,pegstxid,tokenid,accounttxid,myaccount)==0) + { + CCerror = strprintf("cannot find account, you must have an account to liquidate another account!"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (accounttxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,accounttxid,1) != 0) + { + CCerror = strprintf("previous account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (PegsGetAccountRatio(pegstxid,tokenid,liquidatetxid)<(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_TRESHOLD) || PegsGetGlobalRatio(pegstxid)<(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_TRESHOLD)) + { + CCerror = strprintf("not able to liquidate account until account ratio > %lu%% and global ratio > %lu%%",(ASSETCHAINS_PEGSCCPARAMS[0]?ASSETCHAINS_PEGSCCPARAMS[0]:PEGS_ACCOUNT_TRESHOLD),(ASSETCHAINS_PEGSCCPARAMS[1]?ASSETCHAINS_PEGSCCPARAMS[1]:PEGS_GLOBAL_TRESHOLD)); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (liquidatetxid!=zeroid && GetTransaction(liquidatetxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0 || PegsDecodeAccountTx(tx,tmppk,amount,account).empty()) + { + CCerror = strprintf("cannot find account to liquidate or invalid tx %s!",liquidatetxid.GetHex()); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (liquidatetxid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,liquidatetxid,1) != 0) + { + CCerror = strprintf("previous liquidate account tx not yet confirmed"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "current accounttxid=" << accounttxid.GetHex() << " [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + amount=account.first; + burnamount=account.second*0.9; + if ((funds=AddNormalinputs(mtx,mypk,txfee+account.second,64))>=txfee+burnamount) + { + if (liquidatetxid!=zeroid && (pegsfunds=AddPegsInputs(cp,mtx,pegspk,CPubKey(),txfee,1))>=txfee) + { + pegsfunds+=2*CC_MARKER_VALUE; + mtx.vin.push_back(CTxIn(liquidatetxid,0,CScript())); + mtx.vin.push_back(CTxIn(liquidatetxid,1,CScript())); + GetCCaddress1of2(cp,coinaddr,tmppk,pegspk); + CCaddr1of2set(cp,tmppk,pegspk,cp->CCpriv,coinaddr); + if ((tokenfunds=AddPegsTokenInputs(cp,mtx,pegstxid,tokenid,tmppk,pegspk,amount,64))==amount) + { + if (pegsfunds>=txfee+2*CC_MARKER_VALUE) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,pegspk,pegspk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PEGS,CC_MARKER_VALUE,tmppk,pegspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS,(int64_t)(amount*0.95),mypk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_PEGS,amount-(int64_t)(amount*0.95),pegspk)); + mtx.vout.push_back(CTxOut(burnamount,CScript() << ParseHex(HexStr(CCtxidaddr(coinaddr,pegstxid))) << OP_CHECKSIG)); + if (pegsfunds>txfee+2*CC_MARKER_VALUE) mtx.vout.push_back(MakeCC1vout(EVAL_PEGS,pegsfunds-(txfee+2*CC_MARKER_VALUE),pegspk)); + account.first=0; + account.second=0; + LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "new account [deposit=" << account.first << ",debt=" << account.second << "]" << std::endl); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePegsLiquidateOpRet(tokenid,pegstxid,mypk,amount,account))); + } + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("tokens amount in pegs account (%lld) not matching amount in account %lld",tokenfunds,account.first); // this shouldn't happen + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("not enough balance in pegs global CC address"); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + CCerror = strprintf("not enough funds to liquidate account, you must liquidate full debt ammount %lld instead of %lld",txfee+account.second,funds); + LOGSTREAM("pegscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +UniValue PegsAccountHistory(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; std::map> accounts; + std::vector > txids; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsaccounthistory")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + SetCCtxids(txids,coinaddr,true); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second; + if (vout == 1 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("action",PegsDecodeAccountTx(tx,pk,amount,account))); + obj.push_back(Pair("amount",amount)); + obj.push_back(Pair("accounttxid",txid.GetHex())); + obj.push_back(Pair("token",PegsGetTokenName(tmptokenid))); + obj.push_back(Pair("deposit",account.first)); + obj.push_back(Pair("debt",account.second)); + acc.push_back(obj); + } + } + result.push_back(Pair("account history",acc)); return(result); } +UniValue PegsAccountInfo(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmptokenid,tmppegstxid; std::map> accounts; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsaccountinfo")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,mypk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + //LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << std::endl); + if (vout == 1 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tmptokenid))!=0 && pegstxid==tmppegstxid) + { + //LOGSTREAM("pegscc",CCLOG_DEBUG2, stream << "txid=" << txid.GetHex() << ", vout=" << vout << ", nValue=" << nValue << ", tokenid=" << tmptokenid.GetHex() << std::endl); + PegsDecodeAccountTx(tx,pk,amount,account); + accounts[tmptokenid].first=account.first; + accounts[tmptokenid].second=account.second; + } + } + for (std::map>::iterator it = accounts.begin(); it != accounts.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("token",PegsGetTokenName(it->first))); + obj.push_back(Pair("deposit",(double)accounts[it->first].first/COIN)); + obj.push_back(Pair("debt",(double)accounts[it->first].second/COIN)); + if (accounts[it->first].first==0 || accounts[it->first].second==0 || PegsGetTokenPrice(it->first)==0) obj.push_back(Pair("ratio",0)); + else obj.push_back(Pair("ratio",strprintf("%.2f%%",(double)accounts[it->first].second*100/(accounts[it->first].first*PegsGetTokenPrice(it->first))))); + acc.push_back(obj); + } + result.push_back(Pair("account info",acc)); + return(result); +} + +UniValue PegsWorstAccounts(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid,prev; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; double ratio; + std::vector > unspentOutputs; std::pair account; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; std::multimap map; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsworstaccounts")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + if (account.first==0 || account.second==0 || PegsGetTokenPrice(tokenid)==0) ratio=0; + else ratio=(double)account.second*100/(account.first*PegsGetTokenPrice(tokenid)); + if (ratio>80) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("accounttxid",txid.GetHex())); + obj.push_back(Pair("deposit",account.first)); + obj.push_back(Pair("debt",account.second)); + obj.push_back(Pair("ratio",strprintf("%.2f%%",ratio))); + map.insert(std::pair(tokenid,obj)); + } + } + } + std::multimap::iterator it = map.begin(); + for (prev=it->first; it != map.end(); ++it) + { + if (it->first!=prev) + { + result.push_back(Pair(PegsGetTokenName(prev),acc)); + acc.clear(); + prev=it->first; + } + acc.push_back(it->second); + } + result.push_back(Pair(PegsGetTokenName(prev),acc)); + return(result); +} + +UniValue PegsInfo(uint256 pegstxid) +{ + char coinaddr[64]; int64_t nValue,amount; uint256 txid,accounttxid,hashBlock,tmppegstxid,tokenid; + CTransaction tx; int32_t numvouts,vout; char funcid; CPubKey mypk,pegspk,pk; + std::vector > unspentOutputs; std::pair account; + std::map> globalaccounts; double globaldeposit=0; + UniValue result(UniValue::VOBJ),acc(UniValue::VARR); struct CCcontract_info *cp,C; + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","pegsinfo")); + cp = CCinit(&C,EVAL_PEGS); + mypk = pubkey2pk(Mypubkey()); + pegspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,pegspk,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (vout == 0 && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + (funcid=DecodePegsOpRet(tx,tmppegstxid,tokenid))!=0 && pegstxid==tmppegstxid) + { + PegsDecodeAccountTx(tx,pk,amount,account); + globalaccounts[tokenid].first+=account.first; + globalaccounts[tokenid].second+=account.second; + } + } + unspentOutputs.clear(); + GetTokensCCaddress(cp,coinaddr,pegspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && DecodePegsOpRet(tx,tmppegstxid,tokenid)!=0 && pegstxid==tmppegstxid) + { + globalaccounts[tokenid].first+=nValue; + } + } + for (std::map>::iterator it = globalaccounts.begin(); it != globalaccounts.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("token",PegsGetTokenName(it->first))); + obj.push_back(Pair("total deposit",globalaccounts[it->first].first)); + obj.push_back(Pair("total debt",globalaccounts[it->first].second)); + if (globalaccounts[it->first].first==0 || globalaccounts[it->first].second==0 || PegsGetTokenPrice(it->first)==0) obj.push_back(Pair("total ratio",0)); + else obj.push_back(Pair("total ratio",strprintf("%.2f%%",(double)globalaccounts[it->first].second*100/(globalaccounts[it->first].first*PegsGetTokenPrice(it->first))))); + acc.push_back(obj); + } + result.push_back(Pair("info",acc)); + result.push_back(Pair("global ratio",strprintf("%.2f%%",PegsGetGlobalRatio(pegstxid)))); + return(result); +} diff --git a/src/coins.cpp b/src/coins.cpp index f97ba5eee..92206b653 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -604,6 +604,11 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr return 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) + { + nResult = GetCoinImportValue(tx); + continue; + } value = GetOutputFor(tx.vin[i]).nValue; nResult += value; #ifdef KOMODO_ENABLE_INTEREST @@ -675,6 +680,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsMint()) { for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = AccessCoins(prevout.hash); if (!coins || !coins->IsAvailable(prevout.n)) { @@ -696,7 +702,7 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const // use the maximum priority for all (partially or fully) shielded transactions. // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) - if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport()) { + if (tx.vjoinsplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport() || tx.IsPegsImport()) { return MAX_PRIORITY; } diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 921d9a745..c3da613c8 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -75,6 +75,18 @@ CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransacti return CTransaction(mtx); } +CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride) +{ + CMutableTransaction mtx; uint256 accounttxid,pegstxid,tokenid; CScript opret; CScript scriptSig; + + mtx=MakeImportCoinTransaction(proof,burnTx,payouts); + // for spending markers in import tx - to track account state + accounttxid=burnTx.vin[0].prevout.hash; + mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); + mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); + return (mtx); +} + CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector payouts, const std::vector rawproof) { @@ -123,13 +135,30 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb return CTxOut(value, CScript() << OP_RETURN << opret); } +CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account) +{ + std::vector opret; + opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; + ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << pegstxid; + ss << tokenid; + ss << srcpub; + ss << amount; + ss << account); + return CTxOut(value, CScript() << OP_RETURN << opret); +} + bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx, std::vector &payouts) { if (importTx.vout.size() < 1) return false; - if (importTx.vin.size() != 1 || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) { + if ((!importTx.IsPegsImport() && 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; } @@ -263,17 +292,35 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector> amount)); } +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub, int64_t &amount,std::pair &account) +{ + std::vector burnOpret,rawproof; bool isEof=true; + uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; + uint8_t evalCode; + + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> evalCode; + ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> pegstxid; + ss >> tokenid; + ss >> srcpub; + ss >> amount; + ss >> account)); +} /* * Required by main */ CAmount GetCoinImportValue(const CTransaction &tx) { - ImportProof proof; - CTransaction burnTx; - std::vector payouts; - + ImportProof proof; CTransaction burnTx; std::vector payouts; bool isNewImportTx = false; + if ((isNewImportTx = UnmarshalImportTx(tx, proof, burnTx, payouts))) { if (burnTx.vout.size() > 0) { vscript_t vburnOpret; diff --git a/src/importcoin.h b/src/importcoin.h index e016a7c52..955ead825 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -95,16 +95,20 @@ public: CAmount GetCoinImportValue(const CTransaction &tx); CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); +CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector payouts, const std::vector rawproof); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, uint256 bindtxid,std::vector publishers,std::vectortxids,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 payouts,std::vector rawproof,std::string srcaddr, std::string receipt); +CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector payouts,std::vector rawproof,uint256 pegstxid, + uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair account); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt); bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount); +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub,int64_t &amount,std::pair &account); bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx,std::vector &payouts); bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); diff --git a/src/komodo_globals.h b/src/komodo_globals.h index d8e292431..12efd9d83 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -74,7 +74,7 @@ uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAINS_CBOPRET=0; uint64_t ASSETCHAINS_LASTERA = 1; -uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS]; +uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_PEGSCCPARAMS[3]; uint8_t ASSETCHAINS_CCDISABLES[256]; std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 1bf8dfbeb..02bdb07ad 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1692,7 +1692,9 @@ int8_t equihash_params_possible(uint64_t n, uint64_t k) void komodo_args(char *argv0) { - std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[32756],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256], ccEnablesHeight[512] = {0}; + std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[32756],disablebits[32],*extraptr=0; + FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256], ccEnablesHeight[512] = {0}; CTransaction earlytx; uint256 hashBlock; + IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); memset(ccenables,0,sizeof(ccenables)); @@ -1785,6 +1787,11 @@ void komodo_args(char *argv0) printf("KOMODO_REWIND %d\n",KOMODO_REWIND); } KOMODO_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str()); + if (KOMODO_EARLYTXID!=zeroid && (tx_height(KOMODO_EARLYTXID)==0 || tx_height(KOMODO_EARLYTXID)>100)) + { + fprintf(stderr,"earlytx can be only in first 100 blocks or does not exist\n"); + StartShutdown(); + } ASSETCHAINS_EARLYTXIDCONTRACT = GetArg("-ac_earlytxidcontract",0); if ( name.c_str()[0] != 0 ) { @@ -1937,7 +1944,7 @@ void komodo_args(char *argv0) } }*/ } - if ( ASSETCHAINS_BEAMPORT != 0 && ASSETCHAINS_CODAPORT != 0 ) + if ( ASSETCHAINS_BEAMPORT != 0 ) { fprintf(stderr,"can only have one of -ac_beam or -ac_coda\n"); StartShutdown(); @@ -1951,18 +1958,33 @@ void komodo_args(char *argv0) StartShutdown(); } } - else if ( ASSETCHAINS_SELFIMPORT == "BEAM" && ASSETCHAINS_BEAMPORT == 0 ) + else if ( ASSETCHAINS_SELFIMPORT == "BEAM" ) { - fprintf(stderr,"missing -ac_beam for BEAM rpcport\n"); - StartShutdown(); + if (ASSETCHAINS_BEAMPORT == 0) + { + fprintf(stderr,"missing -ac_beam for BEAM rpcport\n"); + StartShutdown(); + } } - else if ( ASSETCHAINS_SELFIMPORT == "CODA" && ASSETCHAINS_CODAPORT == 0 ) + else if ( ASSETCHAINS_SELFIMPORT == "CODA" ) { - fprintf(stderr,"missing -ac_coda for CODA rpcport\n"); - StartShutdown(); + if (ASSETCHAINS_CODAPORT == 0) + { + fprintf(stderr,"missing -ac_coda for CODA rpcport\n"); + StartShutdown(); + } + } + else if ( ASSETCHAINS_SELFIMPORT == "PEGSCC") + { + Split(GetArg("-ac_pegsccparams",""), ASSETCHAINS_PEGSCCPARAMS, 0); + if (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_COMMISSION!=0) + { + fprintf(stderr,"when using import for pegsCC these must be set: -ac_end=1 -ac_perc=0\n"); + StartShutdown(); + } } // else it can be gateway coin - else if (!ASSETCHAINS_SELFIMPORT.empty() && (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_SUPPLY>10 || ASSETCHAINS_COMMISSION!=0)) + else if (!ASSETCHAINS_SELFIMPORT.empty() && (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_SUPPLY>0 || ASSETCHAINS_COMMISSION!=0)) { fprintf(stderr,"when using gateway import these must be set: -ac_end=1 -ac_supply=0 -ac_perc=0\n"); StartShutdown(); diff --git a/src/main.cpp b/src/main.cpp index e01cba61f..ba18fc0e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -735,6 +735,7 @@ bool komodo_dailysnapshot(int32_t height) for (unsigned int j = tx.vin.size(); j-- > 0;) { uint256 blockhash; CTransaction txin; + if (tx.IsPegsImport() && j==0) continue; if ( !tx.IsCoinImport() && !tx.IsCoinBase() && myGetTransaction(tx.vin[j].prevout.hash,txin,blockhash) ) { int vout = tx.vin[j].prevout.n; @@ -1012,6 +1013,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); vector > vSolutions; @@ -1089,6 +1091,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); @@ -1873,7 +1876,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } - if (tx.IsCoinImport()) + if (tx.IsCoinImport() || tx.IsPegsImport()) { // Inverse of normal case; if input exists, it's been spent if (ExistsImportTombstone(tx, view)) @@ -1945,7 +1948,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; - if (!fSkipExpiry && !tx.IsCoinImport()) { + if (!fSkipExpiry && !tx.IsCoinImport() && !tx.IsPegsImport()) { BOOST_FOREACH(const CTxIn &txin, tx.vin) { const CCoins *coins = view.AccessCoins(txin.prevout.hash); if (coins->IsCoinBase()) { @@ -1985,7 +1988,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize) && !tx.IsCoinImport()) + if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize) && !tx.IsCoinImport() && !tx.IsPegsImport()) { static CCriticalSection csFreeLimiter; static double dFreeCount; @@ -2008,7 +2011,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa dFreeCount += nSize; } - if (!tx.IsCoinImport() && fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19) + if (!tx.IsCoinImport() && !tx.IsPegsImport() && fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19) { string errmsg = strprintf("absurdly high fees %s, %d > %d", hash.ToString(), @@ -2689,6 +2692,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund { txundo.vprevout.reserve(tx.vin.size()); BOOST_FOREACH(const CTxIn &txin, tx.vin) { + if (tx.IsPegsImport() && txin.prevout.n==10e8) continue; CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; @@ -2712,7 +2716,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs // Unorthodox state - if (tx.IsCoinImport()) { + if (tx.IsCoinImport() || tx.IsPegsImport()) { // add a tombstone for the burnTx AddImportTombstone(tx, inputs, nHeight); } @@ -2756,6 +2760,11 @@ namespace Consensus { CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) + { + nValueIn=GetCoinImportValue(tx); + continue; + } const COutPoint &prevout = tx.vin[i].prevout; const CCoins *coins = inputs.AccessCoins(prevout.hash); assert(coins); @@ -2867,6 +2876,7 @@ bool ContextualCheckInputs( // still computed and checked, and any change will be caught at the next checkpoint. if (fScriptChecks) { for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.IsPegsImport() && i==0) continue; const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = inputs.AccessCoins(prevout.hash); assert(coins); @@ -2902,7 +2912,7 @@ bool ContextualCheckInputs( } } - if (tx.IsCoinImport()) + if (tx.IsCoinImport() || tx.IsPegsImport()) { LOCK(cs_main); ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata); @@ -3188,10 +3198,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // restore inputs if (!tx.IsMint()) { - const CTxUndo &txundo = blockUndo.vtxundo[i-1]; + CTxUndo &txundo = blockUndo.vtxundo[i-1]; + if (tx.IsPegsImport()) txundo.vprevout.insert(txundo.vprevout.begin(),CTxInUndo()); if (txundo.vprevout.size() != tx.vin.size()) return error("DisconnectBlock(): transaction and undo data inconsistent"); for (unsigned int j = tx.vin.size(); j-- > 0;) { + if (tx.IsPegsImport() && j==0) continue; const COutPoint &out = tx.vin[j].prevout; const CTxInUndo &undo = txundo.vprevout[j]; if (!ApplyTxInUndo(undo, view, out)) @@ -3225,7 +3237,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } - else if (tx.IsCoinImport()) + else if (tx.IsCoinImport() || tx.IsPegsImport()) { RemoveImportTombstone(tx, view); } @@ -3573,6 +3585,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { for (size_t j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); diff --git a/src/miner.cpp b/src/miner.cpp index e4e907447..55e2c708f 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -323,6 +323,13 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 BOOST_FOREACH(const CTxIn& txin, tx.vin) { + if (tx.IsPegsImport() && txin.prevout.n==10e8) + { + CAmount nValueIn = GetCoinImportValue(tx); // burn amount + nTotalIn += nValueIn; + dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. + continue; + } // Read prev transaction if (!view.HaveCoins(txin.prevout.hash)) { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index d97ff03fb..0c20908b0 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -46,6 +46,7 @@ #include "zcash/Proof.hpp" extern uint32_t ASSETCHAINS_MAGIC; +extern std::string ASSETCHAINS_SELFIMPORT; // Overwinter transaction version static const int32_t OVERWINTER_TX_VERSION = 3; @@ -711,6 +712,11 @@ public: return (vin.size() == 1 && vin[0].prevout.n == 10e8); } + bool IsPegsImport() const + { + return (ASSETCHAINS_SELFIMPORT=="PEGSCC" && vin[0].prevout.n == 10e8); + } + friend bool operator==(const CTransaction& a, const CTransaction& b) { return a.hash == b.hash; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 578c2f9ad..f97acf37a 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -206,7 +206,7 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - else if (tx.IsCoinImport()) { + else if (tx.IsCoinImport() && txin.prevout.n==10e8) { in.push_back(Pair("is_import", "1")); ImportProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; if (UnmarshalImportTx(tx, proof, burnTx, payouts)) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 75360fd35..3aea2d13d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -554,6 +554,18 @@ static const CRPCCommand vRPCCommands[] = //{ "tokens", "tokenfillswap", &tokenfillswap, true }, { "tokens", "tokenconvert", &tokenconvert, true }, + // pegs + { "pegs", "pegscreate", &pegscreate, true }, + { "pegs", "pegsfund", &pegsfund, true }, + { "pegs", "pegsget", &pegsget, true }, + { "pegs", "pegsredeem", &pegsredeem, true }, + { "pegs", "pegsliquidate", &pegsliquidate, true }, + { "pegs", "pegsexchange", &pegsexchange, true }, + { "pegs", "pegsaccounthistory", &pegsaccounthistory, true }, + { "pegs", "pegsaccountinfo", &pegsaccountinfo, true }, + { "pegs", "pegsworstaccounts", &pegsworstaccounts, true }, + { "pegs", "pegsinfo", &pegsinfo, true }, + /* Address index */ { "addressindex", "getaddressmempool", &getaddressmempool, true }, { "addressindex", "getaddressutxos", &getaddressutxos, false }, diff --git a/src/rpc/server.h b/src/rpc/server.h index d447d2472..0d6543281 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -349,6 +349,16 @@ extern UniValue FSMcreate(const UniValue& params, bool fHelp); extern UniValue FSMlist(const UniValue& params, bool fHelp); extern UniValue FSMinfo(const UniValue& params, bool fHelp); extern UniValue auctionaddress(const UniValue& params, bool fHelp); +extern UniValue pegscreate(const UniValue& params, bool fHelp); +extern UniValue pegsfund(const UniValue& params, bool fHelp); +extern UniValue pegsget(const UniValue& params, bool fHelp); +extern UniValue pegsredeem(const UniValue& params, bool fHelp); +extern UniValue pegsliquidate(const UniValue& params, bool fHelp); +extern UniValue pegsexchange(const UniValue& params, bool fHelp); +extern UniValue pegsaccounthistory(const UniValue& params, bool fHelp); +extern UniValue pegsaccountinfo(const UniValue& params, bool fHelp); +extern UniValue pegsworstaccounts(const UniValue& params, bool fHelp); +extern UniValue pegsinfo(const UniValue& params, bool fHelp); extern UniValue getnewaddress(const UniValue& params, bool fHelp); // in rpcwallet.cpp //extern UniValue getnewaddress64(const UniValue& params, bool fHelp); // in rpcwallet.cpp diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 2b38d7153..5f0fb2a47 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -121,7 +121,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, const CTransaction& tx = mapTx.find(hash)->GetTx(); if (!tx.IsCoinImport()) { for (unsigned int i = 0; i < tx.vin.size(); i++) + { + if (tx.IsPegsImport() && i==0) continue; mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + } } BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { @@ -147,6 +150,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC uint256 txhash = tx.GetHash(); for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(input); @@ -252,6 +256,7 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac uint256 txhash = tx.GetHash(); for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(input); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ce377d094..d24ec3000 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5336,6 +5336,7 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits #include "../cc/CCHeir.h" #include "../cc/CCMarmara.h" #include "../cc/CCPayments.h" +#include "../cc/CCPegs.h" int32_t ensure_CCrequirements(uint8_t evalcode) { @@ -7973,8 +7974,206 @@ UniValue heirlist(const UniValue& params, bool fHelp) return (HeirList()); } +UniValue pegscreate(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); int32_t i; std::vector txids; + uint8_t N; std::string hex; uint256 txid; int64_t amount; + + if ( fHelp || params.size()<3) + throw runtime_error("pegscreate amount N bindtxid1 [bindtxid2 ...]\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + amount = atof((char *)params[0].get_str().c_str()) * COIN + 0.00000000499999; + N = atoi((char *)params[1].get_str().c_str()); + if ( params.size() < N+1 ) + throw runtime_error("not enough parameters for N gatewaysbind\n"); + for (i=0; i 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegscreate"); + return(result); +} + +UniValue pegsfund(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + if ( fHelp || params.size()!=3) + throw runtime_error("pegsfund pegstxid tokenid amount\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsFund(0,pegstxid,tokenid,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 pegsfund"); + return(result); +} + +UniValue pegsget(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsget pegstxid tokenid amount\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsGet(0,pegstxid,tokenid,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 pegsget"); + return(result); +} + +UniValue pegsredeem(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid; int64_t amount; + + if ( fHelp || params.size()!=2) + throw runtime_error("pegsredem pegstxid tokenid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + hex = PegsRedeem(0,pegstxid,tokenid); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsredeem"); + return(result); +} + +UniValue pegsliquidate(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid,accounttxid; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsliquidate pegstxid tokenid accounttxid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + accounttxid = Parseuint256(params[2].get_str().c_str()); + hex = PegsLiquidate(0,pegstxid,tokenid,accounttxid); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt pegsliquidate"); + return(result); +} + +UniValue pegsexchange(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 pegstxid,tokenid,accounttxid; int64_t amount; + + if ( fHelp || params.size()!=3) + throw runtime_error("pegsliquidate pegstxid tokenid accounttxid\n"); + if ( ensure_CCrequirements(EVAL_PEGS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256(params[0].get_str().c_str()); + tokenid = Parseuint256(params[1].get_str().c_str()); + amount = atof((char *)params[2].get_str().c_str()) * COIN + 0.00000000499999; + hex = PegsExchange(0,pegstxid,tokenid,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 pegsliquidate"); + return(result); +} + +UniValue pegsaccounthistory(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsaccounthistory pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsAccountHistory(pegstxid)); +} + +UniValue pegsaccountinfo(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsaccountinfo pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsAccountInfo(pegstxid)); +} + +UniValue pegsworstaccounts(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsworstaccounts pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsWorstAccounts(pegstxid)); +} + +UniValue pegsinfo(const UniValue& params, bool fHelp) +{ + uint256 pegstxid; + + if ( fHelp || params.size() != 1 ) + throw runtime_error("pegsinfo pegstxid\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + pegstxid = Parseuint256((char *)params[0].get_str().c_str()); + return(PegsInfo(pegstxid)); +} extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp extern UniValue convertpassphrase(const UniValue& params, bool fHelp);