diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index effa837f2..ac5f22c47 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -22,7 +22,7 @@ #include #define PAYMENTS_TXFEE 10000 -#define PAYMENTS_MERGEOFSET 10 // 100? +#define PAYMENTS_MERGEOFSET 60 // 1H extra. extern std::vector > vAddressSnapshot; extern int32_t lastSnapShotHeight; diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 954d2b93a..528e6ec7f 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -137,6 +137,26 @@ uint8_t DecodePaymentsMergeOpRet(CScript scriptPubKey,uint256 &checktxid) return(0); } +CScript EncodePaymentsReleaseOpRet(uint256 checktxid, int64_t amountReleased) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << checktxid << amountReleased); + return(opret); +} + +uint8_t DecodePaymentsReleaseOpRet(CScript scriptPubKey,uint256 &checktxid,int64_t &amountReleased) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid; ss >> amountReleased) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'R' ) + return(f); + } + return(0); +} + CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int64_t totalallocations,std::vector txidoprets) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; @@ -200,7 +220,6 @@ uint8_t DecodePaymentsTokensOpRet(CScript scriptPubKey,int32_t &lockedblocks,int int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr, CScript &ccopret) { char destaddr[64]; - //if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) if ( getCCopret(tx.vout[v].scriptPubKey, ccopret) ) { if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (cmpaddr[0] == 0 || strcmp(destaddr,cmpaddr) == 0) ) @@ -209,22 +228,6 @@ int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t return(0); } -void pub2createtxid(char *str) -{ - int i,n; - char *rev; - n = (int32_t)strlen(str); - rev = (char *)malloc(n + 1); - for (i=0; i txidoprets; bool fHasOpret = false,fIsMerge = false; CPubKey txidpk,Paymentspk; - int32_t top,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; bool fFixedAmount = false; CScript ccopret; - mpz_t mpzTotalAllocations,mpzAllocation; mpz_init(mpzTotalAllocations); - // user marker vout to get the createtxid - if ( tx.vout.size() == 1 ) + char temp[128], txidaddr[64]={0}; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; int8_t funcid=0, fixedAmount=0; + int32_t i,lockedblocks,minrelease,blocksleft,dust = 0, top,bottom=0,minimum=10000; int64_t change,totalallocations,actualtxfee,amountReleased=0; std::vector txidoprets; bool fHasOpret = false,fIsMerge = false; CPubKey txidpk,Paymentspk; + std::vector> excludeScriptPubKeys; bool fFixedAmount = false; CScript ccopret; + mpz_t mpzTotalAllocations,mpzAllocation,mpzCheckamount; + mpz_init(mpzCheckamount); mpz_init(mpzTotalAllocations); + // Check change is in vout[0], and also fetch the ccopret to determine what type of tx this is. txidaddr is unknown, recheck this later. + if ( (change= IsPaymentsvout(cp,tx,0,txidaddr,ccopret)) != 0 && ccopret.size() > 2 ) { - if ( IsPaymentsvout(cp,tx,0,coinaddr,ccopret) != 0 && ccopret.size() > 2 && DecodePaymentsMergeOpRet(ccopret,createtxid) ) - { + // get the checktxid and the amount released if doing release tx. + if ( DecodePaymentsMergeOpRet(ccopret,createtxid) == 'M' ) fIsMerge = true; - } else return(eval->Invalid("not enough vouts")); - } - else if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) - { - scriptpubkey = HexStr(tx.vout[tx.vout.size()-2].scriptPubKey.begin()+2, tx.vout[tx.vout.size()-2].scriptPubKey.end()-1); - fHasOpret = true; - } - else scriptpubkey = HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin()+2,tx.vout[tx.vout.size()-1].scriptPubKey.end()-1); - if ( !fIsMerge ) - { - strcpy(temp, scriptpubkey.c_str()); - pub2createtxid(temp); - createtxid = Parseuint256(temp); - } - //printf("createtxid.%s\n",createtxid.ToString().c_str()); + else if ( DecodePaymentsReleaseOpRet(ccopret,createtxid,amountReleased) != 'R' ) + return(eval->Invalid("could not decode ccopret")); + mpz_set_si(mpzCheckamount,amountReleased); + } else return(eval->Invalid("could not decode ccopret")); // use the createtxid to fetch the tx and all of the plans info. if ( myGetTransaction(createtxid,plantx,blockhash) != 0 && plantx.vout.size() > 0 ) @@ -295,27 +283,32 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & return(eval->Invalid("negative values")); if ( minimum < 10000 ) return(eval->Invalid("minimum must be over 10000")); + if ( amountReleased < minrelease*COIN ) + { + fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amountReleased, (int64_t)minrelease*COIN); + return(eval->Invalid("amount is too small")); + } Paymentspk = GetUnspendable(cp,0); txidpk = CCtxidaddr(txidaddr,createtxid); - GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); + GetCCaddress1of2(cp,txidaddr,Paymentspk,txidpk); //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); if ( !CheckTxFee(tx, PAYMENTS_TXFEE+1, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime, actualtxfee) ) return eval->Invalid("txfee is too high"); - // make sure change is in vout 0 and is paying to the contract address. - if ( (change= IsPaymentsvout(cp,tx,0,coinaddr,ccopret)) == 0 ) - return(eval->Invalid("change is in wrong vout or is wrong tx type")); - + // Check that the change vout is playing the txid address. + if ( IsPaymentsvout(cp,tx,0,txidaddr,ccopret) == 0 ) + return eval->Invalid("change pays wrong address"); + if ( !fIsMerge ) { // Get all the script pubkeys and allocations std::vector allocations; std::vector scriptPubKeys; - int64_t checkallocations = 0; i = 0; if ( funcid == 'C' ) { // normal payment - for (const uint256& txidopret : txidoprets) + int64_t checkallocations = 0; + for ( auto txidopret : txidoprets) { CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) @@ -341,10 +334,14 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & } i++; } + //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); + if ( totalallocations != checkallocations ) + return(eval->Invalid("allocation missmatch")); mpz_set_si(mpzTotalAllocations,totalallocations); } else if ( funcid == 'S' ) { + // snapshot payment if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) return(eval->Invalid("snapshots not activated on this chain")); if ( vAddressSnapshot.size() == 0 ) @@ -363,14 +360,13 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) { auto &address = vAddressSnapshot[j]; - CScript scriptPubKey = GetScriptForDestination(address.second); bool skip = false; + CScript scriptPubKey = GetScriptForDestination(address.second); + bool skip = false; + // skip excluded addresses. for ( auto skipkey : excludeScriptPubKeys ) { if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) - { skip = true; - //fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); - } } if ( !skip ) { @@ -382,13 +378,9 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & mpz_add(mpzTotalAllocations,mpzTotalAllocations,mpzAllocation); mpz_clear(mpzAllocation); } - if ( i+bottom == top ) // we reached top amount to pay, it can be less than this! - break; + if ( i+bottom == top ) + break; // we reached top amount to pay, it can be less than this, if less address exist on chain. } - - // need to check this later on as less can now be paid. - //if ( i != tx.vout.size()-2 ) - // return(eval->Invalid("pays wrong amount of recipients")); } else if ( funcid == 'O' ) { @@ -398,28 +390,25 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //fprintf(stderr, " allocations.size().%li scriptPubKeys.size.%li\n",allocations.size(), scriptPubKeys.size()); if ( (allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size()) ) return(eval->Invalid("missing data cannot validate")); - - //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); - if ( funcid == 'C' && totalallocations != checkallocations ) // only check for normal payments release. - return(eval->Invalid("allocation missmatch")); // Check vouts go to the right place and pay the right amounts. - int64_t amount = 0, checkamount; int32_t n = 0; - checkamount = tx.GetValueOut() - change - actualtxfee - PAYMENTS_TXFEE; - int64_t temptst = mpz_get_si(mpzTotalAllocations); - fprintf(stderr, "validation checkamount.%li totalallocations.%li\n",checkamount,temptst); - mpz_t mpzCheckamount; mpz_init(mpzCheckamount); mpz_set_si(mpzCheckamount,checkamount); - for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) + int64_t amount = 0; int32_t n = 0; + // We place amount released into ccopret, so that these calcualtion are accurate! + // If you change the amount released in the RPC these calcs will be wrong, and validation will fail. + for (i = 1; i < (fHasOpret ? tx.vout.size()-1 : tx.vout.size()); i++) { + int64_t test; if ( scriptPubKeys[n] != tx.vout[i].scriptPubKey ) { fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()).c_str(), HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()).c_str()); return(eval->Invalid("pays wrong address")); } - int64_t test; if ( fFixedAmount ) { - test = checkamount / (top-bottom); + if ( (top-bottom) > 0 ) + test = amountReleased / (top-bottom); + else + return(eval->Invalid("top/bottom range is illegal")); } else { @@ -431,14 +420,14 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & mpz_clear(mpzAllocation); } //fprintf(stderr, "vout %i test.%li nValue.%li\n", i, test, tx.vout[i].nValue); - // I cant fix this! I dont understand the rouncing errors sorry. - if ( test > tx.vout[i].nValue+1 || test < tx.vout[i].nValue-1 ) + if ( test != tx.vout[i].nValue ) { fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); return(eval->Invalid("amounts do not match")); } if ( test < minimum ) { + // prevent anyone being paid the minimum. fprintf(stderr, "vout.%i test.%li vs minimum.%i\n",i, test, minimum); return(eval->Invalid("under minimum size")); } @@ -447,61 +436,57 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & } if ( allocations.size() > n ) { - // need to check that the next allocation was less than 10k sat, otherwise ppl can truncate the tx at any place. + // need to check that the next allocation was less than minimum, otherwise ppl can truncate the tx at any place not paying all elegible addresses. mpz_init(mpzAllocation); mpz_set_si(mpzAllocation,allocations[n+1]); mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); - mpz_tdiv_q (mpzAllocation,mpzAllocation,mpzTotalAllocations); + mpz_tdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); int64_t test = mpz_get_si(mpzAllocation); - if ( test > minimum+1 ) + //fprintf(stderr, "check next vout pays under min: test.%li > minimuim.%i\n", test, minimum); + if ( test > minimum ) return(eval->Invalid("next allocation was not under minimum")); } mpz_clear(mpzTotalAllocations); mpz_clear(mpzCheckamount); - // This is a backup check to make sure there are no extra vouts paying something else! - if ( checkamount != amount ) - return(eval->Invalid("amounts do not match")); - - if ( amount < minrelease*COIN ) - { - fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); - return(eval->Invalid("amount is too small")); - } } // Check vins - i = 0; int32_t dust = 0; - int32_t blocksleft; - BOOST_FOREACH(const CTxIn& vin, tx.vin) + i = 0; + for (auto vin : tx.vin) { CTransaction txin; if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { // check the vin comes from the CC address's - char destaddr[64]; int32_t mergeoffset = 0; CScript opret; uint256 checktxid; - Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); + char fromaddr[64]; int32_t mergeoffset = 0; CScript vinccopret; uint256 checktxid; + Getscriptaddress(fromaddr,txin.vout[vin.prevout.n].scriptPubKey); if ( fIsMerge && txin.vout[vin.prevout.n].nValue < COIN ) dust++; - if ( strcmp(destaddr,coinaddr) != 0 ) + if ( IsPaymentsvout(cp,txin,vin.prevout.n,cp->unspendableCCaddr,vinccopret) != 0 ) { - // if does not come from address its in the global payments adddress and we need to check the opreturn. - uint256 checktxid; int32_t opret_ind; - if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 ) - getCCopret(txin.vout[vin.prevout.n].scriptPubKey,opret); // get op_return from CCvout, - else - opret = txin.vout[opret_ind].scriptPubKey; - if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) + // if from global payments address get ccopret to detemine pays correct plan. + uint256 checktxid; + if ( vinccopret.size() < 2 || DecodePaymentsFundOpRet(vinccopret,checktxid) != 'F' || checktxid != createtxid ) { - fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); + fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s vout.%i\n", i, txin.GetHash().ToString().c_str(), vin.prevout.n); return(eval->Invalid("vin is not paymentsCC type")); } } - else if ( fIsMerge && getCCopret(txin.vout[vin.prevout.n].scriptPubKey,opret) && opret.size() > 2 && DecodePaymentsMergeOpRet(opret,checktxid) == 'M' ) + else if ( IsPaymentsvout(cp,txin,vin.prevout.n,txidaddr,vinccopret) != 0 ) { - mergeoffset = PAYMENTS_MERGEOFSET; - } - //fprintf(stderr, "mergeoffset.%i\n", mergeoffset); + // if in txid address apply merge offset if applicable. + if ( fIsMerge && vinccopret.size() > 2 && DecodePaymentsMergeOpRet(vinccopret,checktxid) == 'M' ) + { + // Apply merge offset to locked blocks, this prevents people spaming payments fund and payments merge to prevent release happening. + mergeoffset = PAYMENTS_MERGEOFSET; + } + } + else // not from global payments plan, or txid address. + return(eval->Invalid("utxo comes from incorrect address")); // check the chain depth vs locked blocks requirement. if ( !payments_lockedblocks(blockhash, lockedblocks+mergeoffset, blocksleft) ) + { + fprintf(stderr, "vin.%i is not elegible for.%i blocks \n",i, blocksleft); return(eval->Invalid("vin not elegible")); + } i++; } else return(eval->Invalid("cant get vin transaction")); } @@ -551,30 +536,21 @@ int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); if ( (vout == 0 || vout == 1) && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( iter == 0 ) - { - CScript opret; uint256 checktxid; int32_t opret_ind; - if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 ) - { - // get op_return from CCvout - getCCopret(vintx.vout[vout].scriptPubKey,opret); - } - else - { - // get op_return from the op_return - opret = vintx.vout[opret_ind].scriptPubKey; - } - if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) - { - fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); - continue; - } - } if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr,ccopret)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { int32_t offset = 0; - if ( ccopret.size() > 2 && DecodePaymentsMergeOpRet(ccopret,checktxid) == 'M' ) - offset = PAYMENTS_MERGEOFSET; + if ( ccopret.size() > 2 ) + { + if ( iter == 0 && (DecodePaymentsFundOpRet(ccopret,checktxid) != 'F' || checktxid != createtxid) ) + { + // global address but not for this plan. + fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); + continue; + } + // increase merge offset, if this is a merge tx, merging merged utxos. + if ( iter == 1 && GetBalance == 4 && DecodePaymentsMergeOpRet(ccopret,checktxid) == 'M' ) + offset = PAYMENTS_MERGEOFSET; + } int32_t tmpblocksleft = 0; if ( fLockedBlocks && !payments_lockedblocks(hashBlock, lockedblocks+offset, tmpblocksleft) ) { @@ -689,7 +665,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { LOCK(cs_main); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock,tokenid; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations=0,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations=0,checkallocations=0,allocation; CTxOut vout; CScript onlyopret,ccopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; int32_t top,bottom=0,blocksleft=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t funcid,fixedAmount=0,skipminimum=0; bool fFixedAmount = false; mpz_t mpzTotalAllocations; mpz_init(mpzTotalAllocations); cJSON *params = payments_reparse(&n,jsonstr); @@ -727,7 +703,10 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) return(result); } txidpk = CCtxidaddr(txidaddr,createtxid); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); + ccopret = EncodePaymentsReleaseOpRet(createtxid, amount); + std::vector> vData = std::vector>(); + if ( makeCCopret(ccopret, vData) ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk,&vData)); //fprintf(stderr, "funcid.%i\n", funcid); if ( funcid == 'C' ) { @@ -812,6 +791,14 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { fFixedAmount = true; } + if ( (top-bottom) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid range top/bottom")); + if ( params != 0 ) + free_json(params); + return(result); + } for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) { auto &address = vAddressSnapshot[j]; @@ -852,10 +839,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { mpz_t mpzValue; mpz_init(mpzValue); if ( fFixedAmount ) - { mtx.vout[i+1].nValue = amount / (top-bottom); - //fprintf(stderr, "amount.%li / top-bottom.%i = value.%li\n", amount, (top-bottom-2), mtx.vout[i+1].nValue); - } else { mpz_set_si(mpzValue,mtx.vout[i+1].nValue); @@ -872,8 +856,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) return(result); } } - //fprintf(stderr, "nValue.%li \n", mtx.vout[i+1].nValue); mpz_clear(mpzValue); + //fprintf(stderr, "[%i] nValue.%li minimum.%i scriptpubkey.%s\n", i, mtx.vout[i+1].nValue, minimum, HexStr(mtx.vout[i+1].scriptPubKey.begin(),mtx.vout[i+1].scriptPubKey.end()).c_str()); if ( mtx.vout[i+1].nValue < minimum ) { if ( skipminimum == 0 ) @@ -887,15 +871,15 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) else { // truncate off any vouts that are less than minimum. - mtx.vout.resize(i); + mtx.vout.resize(i+1); break; } } totalamountsent += mtx.vout[i+1].nValue; } if ( totalamountsent < amount ) newamount = totalamountsent; - int64_t temptst = mpz_get_si(mpzTotalAllocations); - fprintf(stderr, "checkamount RPC.%li totalallocations.%li\n",totalamountsent, temptst); + //int64_t temptst = mpz_get_si(mpzTotalAllocations); + //fprintf(stderr, "checkamount RPC.%li totalallocations.%li\n",totalamountsent, temptst); mpz_clear(mpzAmount); mpz_clear(mpzTotalAllocations); } else @@ -909,8 +893,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) if ( (inputsum= AddPaymentsInputs(true,0,cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,lockedblocks,minrelease,blocksleft)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; - mtx.vout[0].nValue = inputsum - newamount - 2*PAYMENTS_TXFEE; - mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); + mtx.vout[0].nValue = inputsum - newamount - PAYMENTS_TXFEE; // only 1 txfee, so the minimum in this vout is a tx fee. GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); @@ -945,18 +928,20 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); - CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease; std::vector txidoprets; + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,broadcast=0,lockedblocks,minrelease; std::vector txidoprets; int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); - if ( params != 0 && n > 1 && n <= 3 ) + if ( params != 0 && n > 1 && n <= 4 ) { txid = payments_juint256(jitem(params,0)); amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( n == 3 ) useopret = jint(jitem(params,2),0) != 0; + if ( n == 4 ) + broadcast = jint(jitem(params,3),0) != 0; if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid) == 0) ) { result.push_back(Pair("result","error")); @@ -979,18 +964,14 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) } else { - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); opret = EncodePaymentsFundOpRet(txid); - // Use the below one along with other FinalizeCCTx/return, to get the ccvout scriptpubkey - /*std::vector> vData = std::vector>(); + std::vector> vData = std::vector>(); if ( makeCCopret(opret, vData) ) - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); */ + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); } - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); - //rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); // use this one to get ccvout scriptpubkey. + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); if ( params != 0 ) - free_json(params); - //return(payments_rawtxresult(result,rawtx,0)); // disable sending for CCvout, as we only need to decode the tx. + free_json(params); return(payments_rawtxresult(result,rawtx,1)); } else @@ -1041,7 +1022,6 @@ UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr) // encode the checktxid into the end of the ccvout, along with 'M' to flag merge type tx. opret = EncodePaymentsMergeOpRet(createtxid); std::vector> vData = std::vector>(); - // try to pay to diffrent pubkey here... change txidpk. if ( makeCCopret(opret, vData) ) mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,inputsum-PAYMENTS_TXFEE,Paymentspk,txidpk,&vData)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); @@ -1214,6 +1194,14 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } + if ( top-bottom < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid range, top/bottom")); + if ( params != 0 ) + free_json(params); + return(result); + } if ( n > 6 ) { for (i=0; i