diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index c45fbab9e..13c66d358 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -21,6 +21,7 @@ #include #define PAYMENTS_TXFEE 10000 +#define PAYMENTS_MERGEOFSET 10 // 100? extern std::vector > vAddressSnapshot; extern int32_t lastSnapShotHeight; @@ -29,6 +30,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // CCcustom UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index db1ee7475..4ca73b6d7 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -229,7 +229,7 @@ uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,i CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, std::vector>* vData = NULL); CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, std::vector>* vData = NULL); int32_t has_opret(const CTransaction &tx, uint8_t evalcode); -CScript getCCopret(const CScript &scriptPubKey); +bool getCCopret(const CScript &scriptPubKey, CScript &opret); bool makeCCopret(CScript &opret, std::vector> &vData); CC *MakeCCcond1(uint8_t evalcode,CPubKey pk); CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 7435defbf..fe5fade71 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -241,7 +241,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { char coinaddr[64]; GetCCaddress1of2(cp,coinaddr,globalpk,pubkeys[i]); - //fprintf(stderr,"%s + %s -> %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr); + fprintf(stderr,"%s + %s -> %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr); if ( strcmp(destaddr,coinaddr) == 0 ) { privkey = cp->CCpriv; diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index acf0da766..5fe7d662b 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -70,16 +70,20 @@ int32_t has_opret(const CTransaction &tx, uint8_t evalcode) return 0; } -CScript getCCopret(const CScript &scriptPubKey) +bool getCCopret(const CScript &scriptPubKey, CScript &opret) { std::vector> vParams = std::vector>(); - CScript dummy; CScript opret; - if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() == 1 ) + CScript dummy; bool ret = false; + if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0 ) { - //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); - opret = CScript(vParams[0].begin()+6, vParams[0].end()); + ret = true; + if ( vParams.size() == 1) + { + opret = CScript(vParams[0].begin()+6, vParams[0].end()); + //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); + } } - return opret; + return ret; } bool makeCCopret(CScript &opret, std::vector> &vData) diff --git a/src/cc/customcc.cpp b/src/cc/customcc.cpp index a8b0bf871..f7e8e6407 100644 --- a/src/cc/customcc.cpp +++ b/src/cc/customcc.cpp @@ -82,12 +82,12 @@ UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { char expectedaddress[64]; CPubKey pk; - CScript opret; int32_t numvout; + CScript opret; int32_t numvout = 0; if ( has_opret(tx, EVAL_CUSTOM) == 0 ) { std::vector> vParams = std::vector>(); - opret = getCCopret(tx.vout[0].scriptPubKey); - numvout = 1; + if ( getCCopret(tx.vout[0].scriptPubKey,opret) ) + numvout = 1; } else { diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 0b69f79fc..e42da664f 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -157,22 +157,41 @@ uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t & return(0); } -CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys) +CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t minimum,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << bottom << fixedAmount << excludeScriptPubKeys); + if ( (strcmp(ASSETCHAINS_SYMBOL, "CFEKPAY") == 0) ) // exempt for now, remove this after game completed. + { + minimum = 10000; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << bottom << fixedAmount << excludeScriptPubKeys); + } + else + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << minimum << top << bottom << fixedAmount << excludeScriptPubKeys); return(opret); } -uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &top,int32_t &bottom,int8_t &fixedAmount,std::vector> &excludeScriptPubKeys) +uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &minimum,int32_t &top,int32_t &bottom,int8_t &fixedAmount,std::vector> &excludeScriptPubKeys) { 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 >> lockedblocks; ss >> minrelease; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + if ( (strcmp(ASSETCHAINS_SYMBOL, "CFEKPAY") == 0) ) // exempt for now, remove this after game completed. { - if ( e == EVAL_PAYMENTS && f == 'S' ) - return(f); + minimum = 10000; + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'S' ) + return(f); + } + } + else + { + + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> minimum; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'S' ) + return(f); + } } return(0); } @@ -197,10 +216,11 @@ uint8_t DecodePaymentsTokensOpRet(CScript scriptPubKey,int32_t &lockedblocks,int return(0); } -int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) +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 ( 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) ) return(tx.vout[v].nValue); @@ -259,197 +279,216 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // change is/must be in vout[0] // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly - char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; int8_t funcid=0, fixedAmount=0; - int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; - int32_t top,bottom=0; std::vector> excludeScriptPubKeys; bool fFixedAmount = false; + char temp[128], coinaddr[64]={0}, txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; int8_t funcid=0, fixedAmount=0; + int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector 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() < 2 ) - return(eval->Invalid("not enough vouts")); - if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) + if ( tx.vout.size() == 1 ) + { + if ( IsPaymentsvout(cp,tx,0,coinaddr,ccopret) != 0 && ccopret.size() > 2 && DecodePaymentsMergeOpRet(ccopret,createtxid) ) + { + 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); - strcpy(temp, scriptpubkey.c_str()); - pub2createtxid(temp); - createtxid = Parseuint256(temp); + } + 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()); // use the createtxid to fetch the tx and all of the plans info. if ( myGetTransaction(createtxid,plantx,blockhash) != 0 && plantx.vout.size() > 0 ) { - if ( ((funcid= DecodePaymentsOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) + if ( ((funcid= DecodePaymentsOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) { if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) - return(eval->Invalid("negative values")); + return(eval->Invalid("negative values")); + if ( minimum < 10000 ) + return(eval->Invalid("minimum must be over 10000")); Paymentspk = GetUnspendable(cp,0); txidpk = CCtxidaddr(txidaddr,createtxid); GetCCaddress1of2(cp,coinaddr,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) ) return eval->Invalid("txfee is too high"); - // 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) - { - 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' ) - { - scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); - allocations.push_back(allocation); - //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); - checkallocations += allocation; - // if we have an op_return to pay to need to check it exists and is paying the correct opret. - if ( !opret.empty() ) - { - if ( !fHasOpret ) - { - fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); - return(eval->Invalid("missing opret in payments release")); - } - else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) - { - fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); - return(eval->Invalid("pays incorrect opret")); - } - } - } - i++; - } - mpz_set_si(mpzTotalAllocations,totalallocations); - } - else if ( funcid == 'S' ) - { - if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) - return(eval->Invalid("snapshots not activated on this chain")); - if ( vAddressSnapshot.size() == 0 ) - return(eval->Invalid("need first snapshot")); - // need time for TX to me mined before the next snapshot. - if ( top > 3999 ) - return(eval->Invalid("transaction too big")); - if ( fixedAmount == 7 ) - { - // game setting, randomise bottom and top values - fFixedAmount = payments_game(top,bottom); - } - else if ( fixedAmount != 0 ) - { - fFixedAmount = true; - } - for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) - { - auto &address = vAddressSnapshot[j]; - CScript scriptPubKey = GetScriptForDestination(address.second); bool skip = false; - 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 ) - { - mpz_init(mpzAllocation); - i++; - scriptPubKeys.push_back(scriptPubKey); - allocations.push_back(address.first); - mpz_set_si(mpzAllocation,address.first); - 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 != tx.vout.size()-2 ) - return(eval->Invalid("pays wrong amount of recipients")); - } - else if ( funcid == 'O' ) - { - // tokens snapshot. - } - // sanity check to make sure we got all the required info - //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")); - // make sure change is in vout 0 and is paying to the contract address. - if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) + if ( (change= IsPaymentsvout(cp,tx,0,coinaddr,ccopret)) == 0 ) return(eval->Invalid("change is in wrong vout or is wrong tx type")); - // 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 - PAYMENTS_TXFEE; - 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++) + if ( !fIsMerge ) { - if ( scriptPubKeys[n] != tx.vout[i].scriptPubKey ) + // Get all the script pubkeys and allocations + std::vector allocations; + std::vector scriptPubKeys; + int64_t checkallocations = 0; + i = 0; + if ( funcid == 'C' ) { - 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")); + // normal payment + for (const uint256& 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' ) + { + scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); + allocations.push_back(allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + checkallocations += allocation; + // if we have an op_return to pay to need to check it exists and is paying the correct opret. + if ( !opret.empty() ) + { + if ( !fHasOpret ) + { + fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); + return(eval->Invalid("missing opret in payments release")); + } + else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) + { + fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); + return(eval->Invalid("pays incorrect opret")); + } + } + } + i++; + } + mpz_set_si(mpzTotalAllocations,totalallocations); } - int64_t test; - if ( fFixedAmount ) + else if ( funcid == 'S' ) { - test = checkamount / (top-bottom); + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + return(eval->Invalid("snapshots not activated on this chain")); + if ( vAddressSnapshot.size() == 0 ) + return(eval->Invalid("need first snapshot")); + if ( top > 3999 ) + return(eval->Invalid("transaction too big")); + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + fFixedAmount = payments_game(top,bottom); + } + else if ( fixedAmount != 0 ) + { + fFixedAmount = true; + } + for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) + { + auto &address = vAddressSnapshot[j]; + CScript scriptPubKey = GetScriptForDestination(address.second); bool skip = false; + 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 ) + { + mpz_init(mpzAllocation); + i++; + scriptPubKeys.push_back(scriptPubKey); + allocations.push_back(address.first); + mpz_set_si(mpzAllocation,address.first); + 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 != tx.vout.size()-2 ) + return(eval->Invalid("pays wrong amount of recipients")); } - else + else if ( funcid == 'O' ) { - mpz_init(mpzAllocation); - mpz_set_si(mpzAllocation,allocations[n]); - mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); - mpz_cdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); - test = mpz_get_si(mpzAllocation); - mpz_clear(mpzAllocation); + // tokens snapshot. } - // Vairance of 1 sat is allowed, for rounding errors. - if ( test >= tx.vout[i].nValue+1 && test <= tx.vout[i].nValue-1 ) + // sanity check to make sure we got all the required info, skip for merge type tx + //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 - PAYMENTS_TXFEE; + 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++) { - fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + 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); + } + else + { + mpz_init(mpzAllocation); + mpz_set_si(mpzAllocation,allocations[n]); + mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); + mpz_cdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); + test = mpz_get_si(mpzAllocation); + mpz_clear(mpzAllocation); + } + // Vairance of 1 sat is allowed, for rounding errors. + if ( test >= tx.vout[i].nValue+1 && test <= tx.vout[i].nValue-1 ) + { + 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 ) + { + fprintf(stderr, "vout.%i test.%li vs minimum.%i\n",i, test, minimum); + return(eval->Invalid("under minimum size")); + } + amount += tx.vout[i].nValue; + n++; + } + mpz_clear(mpzTotalAllocations); + // 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")); } - amount += tx.vout[i].nValue; - n++; } - mpz_clear(mpzTotalAllocations); - // 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; + i = 0; int32_t dust = 0; int32_t blocksleft; BOOST_FOREACH(const CTxIn& vin, tx.vin) { - CTransaction txin; + CTransaction txin; if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { // check the vin comes from the CC address's - char destaddr[64]; + char destaddr[64]; int32_t mergeoffset = 0; CScript opret; uint256 checktxid; Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); + if ( fIsMerge && txin.vout[vin.prevout.n].nValue < COIN ) + dust++; if ( strcmp(destaddr,coinaddr) != 0 ) { // if does not come from address its in the global payments adddress and we need to check the opreturn. - CScript opret; uint256 checktxid; int32_t opret_ind; + uint256 checktxid; int32_t opret_ind; if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 ) - opret = getCCopret(txin.vout[vin.prevout.n].scriptPubKey); // get op_return from CCvout, + 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 ) @@ -457,13 +496,25 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); 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' ) + { + mergeoffset = PAYMENTS_MERGEOFSET; } + fprintf(stderr, "mergeoffset.%i\n", mergeoffset); // check the chain depth vs locked blocks requirement. - if ( !payments_lockedblocks(blockhash, lockedblocks, blocksleft) ) + if ( !payments_lockedblocks(blockhash, lockedblocks+mergeoffset, blocksleft) ) return(eval->Invalid("vin not elegible")); + i++; } else return(eval->Invalid("cant get vin transaction")); - i++; } + if ( fIsMerge ) + { + if ( i < 2 ) + return(eval->Invalid("must have at least 2 vins to carry out merge")); + else if ( i == dust+1 ) + return(eval->Invalid("cannot merge only dust")); + } } else return(eval->Invalid("create transaction cannot decode")); } else return(eval->Invalid("Could not get contract transaction")); return(true); @@ -474,7 +525,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t lockedblocks,int64_t minrelease,int32_t &blocksleft) { char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t iter,vout,ht,n = 0; - std::vector > unspentOutputs; + std::vector > unspentOutputs; CScript ccopret; std::vector > blocksleft_balance; if ( GetBalance == 0 ) { @@ -509,7 +560,7 @@ int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 ) { // get op_return from CCvout - opret = getCCopret(vintx.vout[vout].scriptPubKey); + getCCopret(vintx.vout[vout].scriptPubKey,opret); } else { @@ -522,13 +573,15 @@ int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract continue; } } - if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr,ccopret)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { int32_t tmpblocksleft = 0; - if ( GetBalance == 0 && total != 0 && maxinputs != 0 ) + if ( (GetBalance == 0 && total != 0 && maxinputs != 0) || GetBalance == 4 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; - if ( fLockedBlocks && !payments_lockedblocks(hashBlock, lockedblocks, tmpblocksleft) ) + if ( nValue < COIN ) + blocksleft++; // count dust with unused variable. + if ( fLockedBlocks && !payments_lockedblocks(hashBlock, lockedblocks+(GetBalance == 4 ? PAYMENTS_MERGEOFSET : 0), tmpblocksleft) ) { blocksleft_balance.push_back(std::make_pair(tmpblocksleft,nValue)); continue; @@ -636,7 +689,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) //int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),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; - int32_t top,bottom=0,blocksleft=0; std::vector> excludeScriptPubKeys; int8_t funcid,fixedAmount=0; bool fFixedAmount = false; + int32_t top,bottom=0,blocksleft=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t funcid,fixedAmount=0; bool fFixedAmount = false; mpz_t mpzTotalAllocations; mpz_init(mpzTotalAllocations); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); @@ -647,7 +700,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 0 ) { - if ( ((funcid= DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) + if ( ((funcid= DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) { if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { @@ -657,6 +710,9 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } + // set minimum size to 10k sat otherwise the tx will be invalid. + if ( minimum < 10000 ) + minimum = 10000; //latestheight = (nextheight - lockedblocks - 1); if ( amount < minrelease*COIN ) { @@ -817,13 +873,14 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } //fprintf(stderr, "nValue.%li \n", mtx.vout[i+1].nValue); mpz_clear(mpzValue); - /* - replace this with default dust threshold of 10ksat - if ( mtx.vout[i+1].nValue < PAYMENTS_TXFEE ) + if ( mtx.vout[i+1].nValue < minimum ) { - newamount += (PAYMENTS_TXFEE - mtx.vout[i+1].nValue); - mtx.vout[i+1].nValue = PAYMENTS_TXFEE; - } */ + result.push_back(Pair("result","error")); + result.push_back(Pair("error","value too small, try releasing a larger amount")); + if ( params != 0 ) + free_json(params); + return(result); + } totalamountsent += mtx.vout[i+1].nValue; } if ( totalamountsent < amount ) newamount = totalamountsent; @@ -879,7 +936,7 @@ 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; - int32_t top,bottom; std::vector> excludeScriptPubKeys; // snapshot + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); @@ -890,7 +947,7 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( n == 3 ) useopret = jint(jitem(params,2),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,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 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,top,excludeScriptPubKeys,tokenid) == 0) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid createtxid")); @@ -942,6 +999,64 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); + CPubKey Paymentspk,mypk,txidpk; uint256 createtxid,hashBlock; int64_t totalallocations,inputsum; CScript opret; CTransaction tx; char txidaddr[64],destaddr[64]; std::string rawtx; + int32_t n,useopret = 0,lockedblocks,minrelease,top,bottom,minimum=10000,blocksleft; std::vector txidoprets; + 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 ) + { + createtxid = payments_juint256(jitem(params,0)); + txidpk = CCtxidaddr(txidaddr,createtxid); + if ( myGetTransaction(createtxid,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,top,excludeScriptPubKeys,tokenid) == 0) ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid createtxid")); + } + else if ( (inputsum= AddPaymentsInputs(true,4,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft)) > 0 && mtx.vin.size() > 1 ) + { + int32_t dust = blocksleft; + if ( mtx.vin.size() != dust+1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot merge only dust")); + } + else + { + // 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>(); + if ( makeCCopret(opret, vData) ) + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,inputsum-PAYMENTS_TXFEE,Paymentspk,&vData)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx; @@ -1062,7 +1177,7 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; - int32_t lockedblocks,minrelease,top,bottom,n,i; std::vector> excludeScriptPubKeys; int8_t fixedAmount; + int32_t lockedblocks,minrelease,top,bottom,n,i,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount; if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) { result.push_back(Pair("result","error")); @@ -1074,10 +1189,11 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) { lockedblocks = juint(jitem(params,0),0); minrelease = juint(jitem(params,1),0); - top = juint(jitem(params,2),0); - bottom = juint(jitem(params,3),0); - fixedAmount = juint(jitem(params,4),0); // fixed amount is a flag set to 0 or 1. It means allocations are equal rather than weighted by address balance. - if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 ) + minimum = juint(jitem(params,2),0); + top = juint(jitem(params,3),0); + bottom = juint(jitem(params,4),0); + fixedAmount = juint(jitem(params,5),0); // fixed amount is a flag, set to 7 does game mode, 0 normal snapshot, anything else fixed allocations. + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || minimum < 0 || fixedAmount < 0 || top > 3999 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter, or top over 3999")); @@ -1085,15 +1201,15 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } - if ( n > 5 ) + if ( n > 6 ) { - for (i=0; i scriptPubKey; int32_t len = strlen(inputhex)/2; scriptPubKey.resize(len); @@ -1106,7 +1222,16 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) { mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys)); + CScript tempopret = EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys); + if ( tempopret.size() > 10000 ) // TODO: Check this! + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","op_return is too big, try with less exclude addresses.")); + if ( params != 0 ) + free_json(params); + return(result); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,tempopret); if ( params != 0 ) free_json(params); return(payments_rawtxresult(result,rawtx,1)); @@ -1127,7 +1252,7 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease,blocksleft=0; std::vector txidoprets; int64_t funds,fundsopret,elegiblefunds,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; - int32_t top,bottom; std::vector> excludeScriptPubKeys; // snapshot + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot uint256 tokenid; int8_t fixedAmount; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) @@ -1183,9 +1308,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("error","too many opreturns")); } else result.push_back(Pair("txidoprets",a)); } - else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys) != 0 ) + else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) != 0 ) { - if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 ) + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 || minimum < 10000 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -1198,7 +1323,8 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) else result.push_back(Pair("plan_type","snapshot")); result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); - result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("minimum",(int64_t)minimum)); result.push_back(Pair("bottom",(int64_t)bottom)); result.push_back(Pair("top",(int64_t)top)); result.push_back(Pair("fixedFlag",(int64_t)fixedAmount)); @@ -1272,7 +1398,7 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock,tokenid; UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations=0; - int32_t top=0,bottom=0; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; + int32_t top=0,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr,true); @@ -1281,9 +1407,9 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) txid = it->first.txhash; if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' || DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys) == 'S' || DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 'O') ) + if ( tx.vout.size() > 0 && (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' || DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 'S' || DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) || bottom < 0 || fixedAmount < 0 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) || bottom < 0 || fixedAmount < 0 || minimum < 10000 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); diff --git a/src/main.h b/src/main.h index d507f9dd6..2592fc657 100644 --- a/src/main.h +++ b/src/main.h @@ -95,7 +95,7 @@ static const unsigned int MAX_TEMPFILE_SIZE = 0x1000000; // 16 MiB 0x8000000 /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ -static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB +static const unsigned int UNDOFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** Maximum number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; /** -par default (number of script-checking threads, 0 = auto) */ diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 414add201..b1359ef15 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -487,6 +487,7 @@ static const CRPCCommand vRPCCommands[] = { "payments", "paymentslist", &payments_list, true }, { "payments", "paymentsinfo", &payments_info, true }, { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsmerge", &payments_merge, true }, { "payments", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index b674ee909..5ec7640f2 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -287,6 +287,7 @@ extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); extern UniValue payments_release(const UniValue& params, bool fHelp); extern UniValue payments_fund(const UniValue& params, bool fHelp); +extern UniValue payments_merge(const UniValue& params, bool fHelp); extern UniValue payments_txidopret(const UniValue& params, bool fHelp); extern UniValue payments_create(const UniValue& params, bool fHelp); extern UniValue payments_airdrop(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 888c60e0e..aff740e31 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5603,6 +5603,19 @@ UniValue payments_fund(const UniValue& params, bool fHelp) return(PaymentsFund(cp,(char *)params[0].get_str().c_str())); } +UniValue payments_merge(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsmerge \"[%22createtxid%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 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); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsMerge(cp,(char *)params[0].get_str().c_str())); +} + UniValue payments_txidopret(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; @@ -5633,7 +5646,7 @@ UniValue payments_airdrop(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) - throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,top,bottom,fixedFlag,%22excludeAddress%22,...,%22excludeAddressN%22]\"\n"); + throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,mintoaddress,top,bottom,fixedFlag,%22excludeAddress%22,...,%22excludeAddressN%22]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 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;