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 "CCinclude.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 uint64_t RewardsCalc(uint64_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 reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100);
81 fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN);
85 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
87 CScript opret; uint8_t evalcode = EVAL_REWARDS;
88 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
92 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
94 std::vector<uint8_t> vopret; uint8_t *script,e,f;
95 GetOpReturnData(scriptPubKey, vopret);
96 script = (uint8_t *)vopret.data();
97 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
99 if ( e == EVAL_REWARDS && f == 'F' )
105 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
107 CScript opret; uint8_t evalcode = EVAL_REWARDS;
108 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
112 uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
114 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; uint64_t APR,minseconds,maxseconds,mindeposit;
115 GetOpReturnData(scriptPubKey, vopret);
116 if ( vopret.size() > 2 )
118 script = (uint8_t *)vopret.data();
119 if ( script[0] == EVAL_REWARDS )
121 if ( script[1] == 'F' )
123 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
127 } else fprintf(stderr,"unmarshal error for F\n");
129 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
131 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
133 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
135 } else fprintf(stderr,"script[0] %02x != EVAL_REWARDS\n",script[0]);
136 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
140 uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
142 char destaddr[64]; uint64_t sbits; uint256 fundingtxid,txid; uint8_t funcid; int32_t numvouts;
143 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= (int32_t)tx.vout.size()) > 0 )
146 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
149 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
150 return(tx.vout[v].nValue);
156 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
158 static uint256 zerohash;
159 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
160 numvins = tx.vin.size();
161 numvouts = tx.vout.size();
162 for (i=0; i<numvins; i++)
164 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
166 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
167 return eval->Invalid("always should find vin, but didnt");
170 if ( hashBlock == zerohash )
171 return eval->Invalid("cant rewards from mempool");
172 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
173 inputs += assetoshis;
177 for (i=0; i<numvouts; i++)
179 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
180 if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
181 outputs += assetoshis;
183 if ( inputs != outputs+txfee )
185 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
186 return eval->Invalid("mismatched inputs != outputs + txfee");
191 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
193 uint256 txid,fundingtxid,hashBlock; uint64_t sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
194 numvins = tx.vin.size();
195 numvouts = tx.vout.size();
196 preventCCvins = preventCCvouts = -1;
198 return eval->Invalid("no vouts");
202 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
204 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
205 return eval->Invalid("cant find fundingtxid");
206 else if ( fundingTx.vout.size() > 0 && DecodeRewardsFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 'F' )
207 return eval->Invalid("fundingTx not valid");
211 //vins.*: normal inputs
212 //vout.0: CC vout for funding
213 //vout.1: normal marker vout for easy searching
214 //vout.2: normal change
215 //vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
216 return eval->Invalid("unexpected RewardsValidate for createfunding");
219 //vins.*: normal inputs
220 //vout.0: CC vout for funding
221 //vout.1: normal change
222 //vout.n-1: opreturn 'A' sbits fundingtxid
223 return eval->Invalid("unexpected RewardsValidate for addfunding");
226 //vins.*: normal inputs
227 //vout.0: CC vout for locked funds
228 //vout.1: normal output to unlock address
230 //vout.n-1: opreturn 'L' sbits fundingtxid
231 return eval->Invalid("unexpected RewardsValidate for lock");
234 //vin.0: locked funds CC vout.0 from lock
235 //vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
236 //vout.0: funding CC change
237 //vout.1: normal output to unlock address
238 //vout.n-1: opreturn 'U' sbits fundingtxid
239 for (i=0; i<numvins; i++)
241 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
242 return eval->Invalid("unexpected normal vin for unlock");
244 if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 )
246 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
247 return eval->Invalid("always should find vin.0, but didnt");
248 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
249 return eval->Invalid("lock tx vout.0 is normal output");
250 else if ( tx.vout.size() < 3 )
251 return eval->Invalid("unlock tx not enough vouts");
252 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
253 return eval->Invalid("unlock tx vout.0 is normal output");
254 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
255 return eval->Invalid("unlock tx vout.1 is CC output");
256 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
257 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
258 amount = vinTx.vout[0].nValue;
259 reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
260 if ( tx.vout[1].nValue > amount+reward )
261 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
266 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
271 // 'L' vs 'F' and 'A'
272 uint64_t AddRewardsInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
274 char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid;
275 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
276 GetCCaddress(cp,coinaddr,pk);
277 SetCCunspents(unspentOutputs,coinaddr);
278 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
280 txid = it->first.txhash;
281 vout = (int32_t)it->first.index;
282 if ( it->second.satoshis < 1000000 )
284 fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
285 for (j=0; j<mtx.vin.size(); j++)
286 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
288 if ( j != mtx.vin.size() )
290 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
292 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
294 if ( sbits != refsbits || fundingtxid != reffundingtxid )
296 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
297 if ( fundsflag != 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
299 else if ( fundsflag == 0 && (funcid != 'L' || tx.vout.size() < 4) )
301 if ( total != 0 && maxinputs != 0 )
303 if ( fundsflag == 0 )
304 scriptPubKey = tx.vout[1].scriptPubKey;
305 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
307 totalinputs += it->second.satoshis;
309 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
311 } else fprintf(stderr,"null funcid\n");
317 uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
319 char coinaddr[64]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
320 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
321 GetCCaddress(cp,coinaddr,pk);
322 SetCCunspents(unspentOutputs,coinaddr);
323 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
325 txid = it->first.txhash;
326 vout = (int32_t)it->first.index;
327 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
329 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
331 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
333 if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout,sbits,fundingtxid)) > 0 )
334 totalinputs += nValue;
335 else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
336 } //else fprintf(stderr,"else case\n");
337 } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
343 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
345 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
346 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
347 GetCCaddress(cp,CCaddr,rewardspk);
348 SetCCtxids(txids,CCaddr);
349 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
351 //int height = it->first.blockHeight;
352 txid = it->first.txhash;
353 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
355 //char str[65]; fprintf(stderr,"rewards plan %s\n",uint256_str(str,txid));
356 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
358 if ( sbits == refsbits )
366 UniValue RewardsInfo(uint256 rewardsid)
368 UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; uint64_t APR,minseconds,maxseconds,mindeposit,sbits,funding; CPubKey rewardspk; struct CCcontract_info *cp,C; char str[67],numstr[65];
369 if ( GetTransaction(rewardsid,vintx,hashBlock,false) == 0 )
371 fprintf(stderr,"cant find fundingtxid\n");
372 result.push_back(Pair("error","cant find fundingtxid"));
375 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
377 fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
378 result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
381 result.push_back(Pair("result","success"));
382 result.push_back(Pair("fundingtxid",uint256_str(str,rewardsid)));
383 unstringbits(str,sbits);
384 result.push_back(Pair("name",str));
385 result.push_back(Pair("sbits",sbits));
386 sprintf(numstr,"%.8f",(double)APR/COIN);
387 result.push_back(Pair("APR",numstr));
388 result.push_back(Pair("minseconds",minseconds));
389 result.push_back(Pair("maxseconds",maxseconds));
390 sprintf(numstr,"%.8f",(double)mindeposit/COIN);
391 result.push_back(Pair("mindeposit",numstr));
392 cp = CCinit(&C,EVAL_REWARDS);
393 rewardspk = GetUnspendable(cp,0);
394 funding = RewardsPlanFunds(sbits,cp,rewardspk,rewardsid);
395 sprintf(numstr,"%.8f",(double)funding/COIN);
396 result.push_back(Pair("funding",numstr));
400 UniValue RewardsList()
402 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];
403 cp = CCinit(&C,EVAL_REWARDS);
404 SetCCtxids(addressIndex,cp->normaladdr);
405 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
407 txid = it->first.txhash;
408 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
410 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
412 result.push_back(uint256_str(str,txid));
419 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
421 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
422 if ( funds < COIN || mindeposit < 0 || minseconds < 0 || maxseconds < 0 )
424 fprintf(stderr,"negative parameter error\n");
427 cp = CCinit(&C,EVAL_REWARDS);
430 mypk = pubkey2pk(Mypubkey());
431 rewardspk = GetUnspendable(cp,0);
432 sbits = stringbits(planstr);
433 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
435 fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
438 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
440 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
441 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
442 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
444 fprintf(stderr,"cant find enough inputs\n");
448 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
450 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
453 fprintf(stderr,"negative parameter error\n");
456 cp = CCinit(&C,EVAL_REWARDS);
459 mypk = pubkey2pk(Mypubkey());
460 rewardspk = GetUnspendable(cp,0);
461 sbits = stringbits(planstr);
462 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
464 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
467 sbits = stringbits(planstr);
468 if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
470 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
471 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
472 } else fprintf(stderr,"cant find enough inputs\n");
473 fprintf(stderr,"cant find fundingtxid\n");
477 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
479 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
482 fprintf(stderr,"negative parameter error\n");
485 cp = CCinit(&C,EVAL_REWARDS);
488 mypk = pubkey2pk(Mypubkey());
489 rewardspk = GetUnspendable(cp,0);
490 sbits = stringbits(planstr);
491 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
493 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
496 if ( deposit < mindeposit )
498 fprintf(stderr,"Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
501 if ( (funding= RewardsPlanFunds(sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
503 if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
505 mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
506 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
507 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
508 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)deposit/COIN);
510 fprintf(stderr,"cant find rewards inputs\n");
514 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
516 CMutableTransaction mtx; CTransaction tx; char coinaddr[64]; CPubKey mypk,rewardspk; CScript opret,scriptPubKey,ignore; uint256 hashBlock; uint64_t funding,sbits,reward=0,amount=0,inputs,CCchange=0,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
517 cp = CCinit(&C,EVAL_REWARDS);
520 rewardspk = GetUnspendable(cp,0);
521 mypk = pubkey2pk(Mypubkey());
522 sbits = stringbits(planstr);
523 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
525 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
528 fprintf(stderr,"APR %.8f minseconds.%llu maxseconds.%llu mindeposit %.8f\n",(double)APR/COIN,(long long)minseconds,(long long)maxseconds,(double)mindeposit/COIN);
529 if ( locktxid == zeroid )
530 amount = AddRewardsInputs(scriptPubKey,0,cp,mtx,rewardspk,(1LL << 30),1,sbits,fundingtxid);
533 GetCCaddress(cp,coinaddr,rewardspk);
534 if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
536 fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
539 if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
541 scriptPubKey = tx.vout[1].scriptPubKey;
542 mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
546 fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
550 if ( amount > 0 && (reward= RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit)) > txfee && scriptPubKey.size() > 0 )
552 if ( (inputs= AddRewardsInputs(ignore,1,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) > 0 )
554 if ( inputs >= (reward + 2*txfee) )
555 CCchange = (inputs - (reward + txfee));
556 fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN);
557 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
558 mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey));
559 return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
561 fprintf(stderr,"cant find enough rewards inputs\n");
563 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);