1 /******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
4 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
5 * the top-level directory of this distribution for the individual copyright *
6 * holder information and the developer policies on copyright and licensing. *
8 * Unless otherwise agreed in a custom licensing agreement, no part of the *
9 * SuperNET software, including this file may be copied, modified, propagated *
10 * or distributed except according to the terms contained in the LICENSE file *
12 * Removal or modification of this copyright notice is prohibited. *
14 ******************************************************************************/
16 #include "CCrewards.h"
19 The rewards CC contract is initially for OOT, which needs this functionality. However, many of the attributes can be parameterized to allow different rewards programs to run. Multiple rewards plans could even run on the same blockchain, though the user would need to choose which one to lock funds into.
21 At the high level, the user would lock funds for some amount of time and at the end of it, would get all the locked funds back with an additional reward. So there needs to be a lock funds and unlock funds ability. Additionally, the rewards need to come from somewhere, so similar to the faucet, there would be a way to fund the reward.
23 Additional requirements are for the user to be able to lock funds via SPV. This requirement in turns forces the creation of a way for anybody to be able to unlock the funds as that operation requires a native daemon running and cant be done over SPV. The idea is to allow anybody to run a script that would unlock all funds that are matured. As far as the user is concerned, he locks his funds via SPV and after some time it comes back with an extra reward.
25 In reality, the funds are locked into a CC address that is unspendable, except for some special conditions and it needs to come back to the address that funded the lock. In order to implement this, several things are clear.
27 1) each locked CC utxo needs to be linked to a specific rewards plan
28 2) each locked CC utxo needs to know the only address that it can be unlocked into
29 3) SPV requirement means the lock transaction needs to be able to be created without any CC signing
31 The opreturn will be used to store the name of the rewards plan and all funding and locked funds with the same plan will use the same pool of rewards. plan names will be limited to 8 chars and encoded into a uint64_t.
33 The initial funding transaction will have all the parameters for the rewards plan encoded in the vouts. Additional fundings will just increase the available CC utxo for the rewards.
35 Locks wont have any CC vins, but will send to the RewardsCCaddress, with the plan stringbits in the opreturn. vout1 will have the unlock address and no other destination is valid.
37 Unlock does a CC spend to the vout1 address
42 vout.0: CC vout for funding
43 vout.1: normal marker vout for easy searching
45 vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
49 vout.0: CC vout for funding
51 vout.n-1: opreturn 'A' sbits fundingtxid
55 vout.0: CC vout for locked funds
56 vout.1: normal output to unlock address
58 vout.n-1: opreturn 'L' sbits fundingtxid
61 vin.0: locked funds CC vout.0 from lock
62 vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
63 vout.0: funding CC change
64 vout.1: normal output to unlock address
65 vout.n-1: opreturn 'U' sbits fundingtxid
69 int64_t RewardsCalc(int64_t amount,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
71 int32_t numblocks; uint64_t duration,reward = 0;
72 //fprintf(stderr,"minseconds %llu maxseconds %llu\n",(long long)minseconds,(long long)maxseconds);
73 if ( (duration= CCduration(numblocks,txid)) < minseconds )
75 fprintf(stderr,"duration %llu < minseconds %llu\n",(long long)duration,(long long)minseconds);
77 //duration = (uint32_t)time(NULL) - (1532713903 - 3600 * 24);
78 } else if ( duration > maxseconds )
79 duration = maxseconds;
80 if ( 0 ) // amount * APR * duration / COIN * 100 * 365*24*3600
81 reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100);
82 else reward = (((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000)) / 10000;
83 if ( reward > amount )
85 fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f vals %.8f %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN,(double)((amount * duration) / (365 * 24 * 3600LL))/COIN,(double)(((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000))/COIN);
89 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
91 CScript opret; uint8_t evalcode = EVAL_REWARDS;
92 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
96 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
98 std::vector<uint8_t> vopret; uint8_t *script,e,f;
99 GetOpReturnData(scriptPubKey, vopret);
100 script = (uint8_t *)vopret.data();
101 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
103 if ( e == EVAL_REWARDS && f == 'F' )
109 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
111 CScript opret; uint8_t evalcode = EVAL_REWARDS;
112 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
116 uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
118 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; uint64_t APR,minseconds,maxseconds,mindeposit;
119 GetOpReturnData(scriptPubKey, vopret);
120 if ( vopret.size() > 2 )
122 script = (uint8_t *)vopret.data();
123 if ( script[0] == EVAL_REWARDS )
125 if ( script[1] == 'F' )
127 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
131 } else fprintf(stderr,"unmarshal error for F\n");
133 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
135 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
137 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
139 } else fprintf(stderr,"script[0] %02x != EVAL_REWARDS\n",script[0]);
140 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
144 int64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
146 char destaddr[64]; uint64_t sbits; uint256 fundingtxid,txid; uint8_t funcid; int32_t numvouts;
147 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= (int32_t)tx.vout.size()) > 0 )
150 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
153 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
154 return(tx.vout[v].nValue);
160 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
162 static uint256 zerohash;
163 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
164 numvins = tx.vin.size();
165 numvouts = tx.vout.size();
166 for (i=0; i<numvins; i++)
168 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
170 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
171 return eval->Invalid("always should find vin, but didnt");
174 if ( hashBlock == zerohash )
175 return eval->Invalid("cant rewards from mempool");
176 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
177 inputs += assetoshis;
181 for (i=0; i<numvouts; i++)
183 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
184 if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
185 outputs += assetoshis;
187 if ( inputs != outputs+txfee )
189 fprintf(stderr,"inputs %llu vs outputs %llu txfee %llu\n",(long long)inputs,(long long)outputs,(long long)txfee);
190 return eval->Invalid("mismatched inputs != outputs + txfee");
195 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn, bool fulfilled)
197 uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
198 numvins = tx.vin.size();
199 numvouts = tx.vout.size();
200 preventCCvins = preventCCvouts = -1;
202 return eval->Invalid("no vouts");
206 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
208 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
209 return eval->Invalid("cant find fundingtxid");
210 else if ( fundingTx.vout.size() > 0 && DecodeRewardsFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 'F' )
211 return eval->Invalid("fundingTx not valid");
212 if ( APR > REWARDSCC_MAXAPR )
213 return eval->Invalid("excessive APR");
217 //vins.*: normal inputs
218 //vout.0: CC vout for funding
219 //vout.1: normal marker vout for easy searching
220 //vout.2: normal change
221 //vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
222 return eval->Invalid("unexpected RewardsValidate for createfunding");
225 //vins.*: normal inputs
226 //vout.0: CC vout for funding
227 //vout.1: normal change
228 //vout.n-1: opreturn 'A' sbits fundingtxid
229 return eval->Invalid("unexpected RewardsValidate for addfunding");
232 //vins.*: normal inputs
233 //vout.0: CC vout for locked funds
234 //vout.1: normal output to unlock address
236 //vout.n-1: opreturn 'L' sbits fundingtxid
237 return eval->Invalid("unexpected RewardsValidate for lock");
240 //vin.0: locked funds CC vout.0 from lock
241 //vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
242 //vout.0: funding CC change or recover normal payout
243 //vout.1: normal output to unlock address
244 //vout.n-1: opreturn 'U' sbits fundingtxid
245 if ( fundingtxid == tx.vin[0].prevout.hash )
246 return eval->Invalid("cant unlock fundingtxid");
247 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
248 return eval->Invalid("always should find vin.0, but didnt");
249 else if ( DecodeRewardsOpRet(tx.vin[0].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid) != 'L' )
250 return eval->Invalid("can only unlock locktxid");
251 else if ( fundingtxid != vinfundingtxid )
252 return eval->Invalid("mismatched vinfundingtxid");
253 for (i=0; i<numvins; i++)
255 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
256 return eval->Invalid("unexpected normal vin for unlock");
258 if ( numvouts == 2 && numvins == 1 )
260 if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
261 return eval->Invalid("unlock recover tx vout.0 is not normal output");
262 else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey )
263 return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey");
264 else if ( tx.vout[0].nValue > vinTx.vout[0].nValue )
265 return eval->Invalid("unlock recover tx vout.0 mismatched amounts");
266 else if ( tx.vout[1].nValue > 0 )
267 return eval->Invalid("unlock recover tx vout.1 nonz amount");
270 if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
271 return eval->Invalid("unlock tx vout.0 is normal output");
272 else if ( numvouts != 3 )
273 return eval->Invalid("unlock tx wrong number of vouts");
274 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
275 return eval->Invalid("unlock tx vout.0 is normal output");
276 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
277 return eval->Invalid("unlock tx vout.1 is CC output");
278 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
279 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
280 amount = vinTx.vout[0].nValue;
281 reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
282 if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 )
284 else if ( tx.vout[1].nValue > amount+reward )
285 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
286 else if ( tx.vout[2].nValue > 0 )
287 return eval->Invalid("unlock tx vout.2 isnt 0");
292 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
297 static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t refsbits,uint256 reffundingtxid,uint64_t needed)
299 uint8_t funcid; uint64_t sbits,nValue; uint256 fundingtxid; char str[65];
300 memset(&txid,0,sizeof(txid));
303 BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
305 const CTransaction &tx = e.GetTx();
306 if ( tx.vout.size() > 0 && tx.vout[0].nValue >= needed )
308 const uint256 &hash = tx.GetHash();
309 if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(hash,0) == 0 )
311 if ( (funcid= DecodeRewardsOpRet(hash,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'U' && sbits == refsbits && fundingtxid == reffundingtxid )
315 nValue = tx.vout[0].nValue;
316 fprintf(stderr,"found 'U' %s %.8f in unspent in mempool\n",uint256_str(str,txid),(double)nValue/COIN);
325 // 'L' vs 'F' and 'A'
326 int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
328 char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid;
329 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
330 GetCCaddress(cp,coinaddr,pk);
331 SetCCunspents(unspentOutputs,coinaddr);
332 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
334 txid = it->first.txhash;
335 vout = (int32_t)it->first.index;
336 if ( it->second.satoshis < 1000000 )
338 //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
339 for (j=0; j<mtx.vin.size(); j++)
340 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
342 if ( j != mtx.vin.size() )
344 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
346 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
348 if ( sbits != refsbits || fundingtxid != reffundingtxid )
350 if ( maxseconds == 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
352 else if ( maxseconds != 0 && funcid != 'L' )
354 if ( CCduration(numblocks,txid) < maxseconds )
357 fprintf(stderr,"maxseconds.%d (%c) %.8f %.8f\n",(int32_t)maxseconds,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
358 if ( total != 0 && maxinputs != 0 )
360 if ( maxseconds != 0 )
361 scriptPubKey = tx.vout[1].scriptPubKey;
362 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
364 totalinputs += it->second.satoshis;
366 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
368 } else fprintf(stderr,"null funcid\n");
371 if ( maxseconds == 0 && totalinputs < total && (maxinputs == 0 || n < maxinputs-1) )
373 fprintf(stderr,"search mempool for unlocked and unspent CC rewards output for %.8f\n",(double)(total-totalinputs)/COIN);
374 if ( (nValue= myIs_unlockedtx_inmempool(txid,vout,refsbits,reffundingtxid,total-totalinputs)) > 0 )
376 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
377 fprintf(stderr,"added mempool vout for %.8f\n",(double)nValue/COIN);
378 totalinputs += nValue;
385 int64_t RewardsPlanFunds(uint64_t &lockedfunds,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
387 char coinaddr[64]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
388 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
390 GetCCaddress(cp,coinaddr,pk);
391 SetCCunspents(unspentOutputs,coinaddr);
392 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
394 txid = it->first.txhash;
395 vout = (int32_t)it->first.index;
396 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
398 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'A' || funcid == 'U' || funcid == 'L' )
400 if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
402 if ( (nValue= IsRewardsvout(cp,tx,vout,sbits,fundingtxid)) > 0 )
405 lockedfunds += nValue;
406 else totalinputs += nValue;
408 else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
409 } //else fprintf(stderr,"else case\n");
410 } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
416 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
418 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
419 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
420 GetCCaddress(cp,CCaddr,rewardspk);
421 SetCCtxids(txids,CCaddr);
422 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
424 //int height = it->first.blockHeight;
425 txid = it->first.txhash;
426 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
428 //char str[65]; fprintf(stderr,"rewards plan %s\n",uint256_str(str,txid));
429 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
431 if ( sbits == refsbits )
439 UniValue RewardsInfo(uint256 rewardsid)
441 UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; uint64_t lockedfunds,APR,minseconds,maxseconds,mindeposit,sbits,funding; CPubKey rewardspk; struct CCcontract_info *cp,C; char str[67],numstr[65];
442 if ( GetTransaction(rewardsid,vintx,hashBlock,false) == 0 )
444 fprintf(stderr,"cant find fundingtxid\n");
445 result.push_back(Pair("result","error"));
446 result.push_back(Pair("error","cant find fundingtxid"));
449 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
451 fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
452 result.push_back(Pair("result","error"));
453 result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
456 result.push_back(Pair("result","success"));
457 result.push_back(Pair("fundingtxid",uint256_str(str,rewardsid)));
458 unstringbits(str,sbits);
459 result.push_back(Pair("name",str));
460 result.push_back(Pair("sbits",sbits));
461 sprintf(numstr,"%.8f",(double)APR/COIN);
462 result.push_back(Pair("APR",numstr));
463 result.push_back(Pair("minseconds",minseconds));
464 result.push_back(Pair("maxseconds",maxseconds));
465 sprintf(numstr,"%.8f",(double)mindeposit/COIN);
466 result.push_back(Pair("mindeposit",numstr));
467 cp = CCinit(&C,EVAL_REWARDS);
468 rewardspk = GetUnspendable(cp,0);
469 funding = RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,rewardsid);
470 sprintf(numstr,"%.8f",(double)funding/COIN);
471 result.push_back(Pair("funding",numstr));
472 sprintf(numstr,"%.8f",(double)lockedfunds/COIN);
473 result.push_back(Pair("locked",numstr));
477 UniValue RewardsList()
479 UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; char str[65];
480 cp = CCinit(&C,EVAL_REWARDS);
481 SetCCtxids(addressIndex,cp->normaladdr);
482 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
484 txid = it->first.txhash;
485 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
487 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
489 result.push_back(uint256_str(str,txid));
496 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
498 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
499 if ( funds < COIN || mindeposit < 0 || minseconds < 0 || maxseconds < 0 )
501 fprintf(stderr,"negative parameter error\n");
504 if ( APR > REWARDSCC_MAXAPR )
506 fprintf(stderr,"25%% APR is maximum\n");
509 cp = CCinit(&C,EVAL_REWARDS);
512 mypk = pubkey2pk(Mypubkey());
513 rewardspk = GetUnspendable(cp,0);
514 sbits = stringbits(planstr);
515 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
517 fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
520 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
522 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
523 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
524 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
526 fprintf(stderr,"cant find enough inputs\n");
530 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
532 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
535 fprintf(stderr,"negative parameter error\n");
538 cp = CCinit(&C,EVAL_REWARDS);
541 mypk = pubkey2pk(Mypubkey());
542 rewardspk = GetUnspendable(cp,0);
543 sbits = stringbits(planstr);
544 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
546 CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
547 fprintf(stderr,"%s\n",CCerror.c_str());
550 sbits = stringbits(planstr);
551 if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
553 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
554 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
556 CCerror = "cant find enough inputs";
557 fprintf(stderr,"%s\n", CCerror.c_str());
559 CCerror = "cant find fundingtxid";
560 fprintf(stderr,"%s\n", CCerror.c_str());
564 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
566 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t lockedfunds,sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
567 if ( deposit < txfee )
569 CCerror = "deposit amount less than txfee";
570 fprintf(stderr,"%s\n",CCerror.c_str());
573 cp = CCinit(&C,EVAL_REWARDS);
576 mypk = pubkey2pk(Mypubkey());
577 rewardspk = GetUnspendable(cp,0);
578 sbits = stringbits(planstr);
579 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
581 CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
582 fprintf(stderr,"%s\n",CCerror.c_str());
585 if ( deposit < mindeposit )
587 CCerror = strprintf("Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
588 fprintf(stderr,"%s\n",CCerror.c_str());
591 if ( (funding= RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
593 if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
595 mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
596 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
597 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
599 CCerror = strprintf("cant find enough inputs %.8f not enough for %.8f, make sure you imported privkey for the -pubkey address\n",(double)funding/COIN,(double)deposit/COIN);
600 fprintf(stderr,"%s\n",CCerror.c_str());
603 fprintf(stderr,"cant find rewards inputs funding %.8f locked %.8f vs deposit %.8f\n",(double)funding/COIN,(double)lockedfunds/COIN,(double)deposit/COIN);
607 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
609 CMutableTransaction mtx,firstmtx; CTransaction tx; char coinaddr[64]; CPubKey mypk,rewardspk; CScript scriptPubKey,ignore; uint256 hashBlock; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; int64_t funding,reward=0,amount=0,inputs,CCchange=0; struct CCcontract_info *cp,C;
610 cp = CCinit(&C,EVAL_REWARDS);
613 rewardspk = GetUnspendable(cp,0);
614 mypk = pubkey2pk(Mypubkey());
615 sbits = stringbits(planstr);
616 if ( locktxid == fundingtxid )
618 fprintf(stderr,"Rewards plan cant unlock fundingtxid\n");
619 CCerror = "Rewards plan cant unlock fundingtxid";
622 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
624 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
625 CCerror = "Rewards plan does not exist";
628 fprintf(stderr,"APR %.8f minseconds.%llu maxseconds.%llu mindeposit %.8f\n",(double)APR/COIN,(long long)minseconds,(long long)maxseconds,(double)mindeposit/COIN);
629 if ( locktxid == zeroid )
630 amount = AddRewardsInputs(scriptPubKey,maxseconds,cp,mtx,rewardspk,(1LL << 30),1,sbits,fundingtxid);
633 GetCCaddress(cp,coinaddr,rewardspk);
634 if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
636 fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
637 CCerror = "locktxid/v0 is spent";
640 if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
642 scriptPubKey = tx.vout[1].scriptPubKey;
643 mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
647 fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
648 CCerror = "no normal vout.1 in locktxid";
652 if ( amount > txfee )
654 reward = RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
655 if ( scriptPubKey.size() > 0 )
657 if ( reward > txfee )
660 if ( (inputs= AddRewardsInputs(ignore,0,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) >= reward+txfee )
662 if ( inputs >= (reward + 2*txfee) )
663 CCchange = (inputs - (reward + txfee));
664 fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN);
665 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
666 mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey));
667 return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
671 firstmtx.vout.push_back(CTxOut(amount-txfee,scriptPubKey));
672 //CCerror = "cant find enough rewards inputs";
673 fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN);
674 return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
679 CCerror = strprintf("reward %.8f is <= the transaction fee", reward);
680 fprintf(stderr,"%s\n", CCerror.c_str());
685 CCerror = "invalid scriptPubKey";
686 fprintf(stderr,"%s\n", CCerror.c_str());
691 CCerror = "amount must be more than txfee";
692 fprintf(stderr,"%s\n", CCerror.c_str());
694 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);