]> Git Repo - VerusCoin.git/blob - src/cc/rewards.cpp
Require sbits/fundingtxid matches for rewards vins
[VerusCoin.git] / src / cc / rewards.cpp
1 /******************************************************************************
2  * Copyright © 2014-2018 The SuperNET Developers.                             *
3  *                                                                            *
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.  *
7  *                                                                            *
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 *
11  *                                                                            *
12  * Removal or modification of this copyright notice is prohibited.            *
13  *                                                                            *
14  ******************************************************************************/
15
16 #include "CCinclude.h"
17
18 /*
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.
20  
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.
22  
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.
24  
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.
26  
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
30  
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.
32  
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.
34  
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.
36  
37  Unlock does a CC spend to the vout1 address
38  
39  
40  createfunding
41  vins.*: normal inputs
42  vout.0: CC vout for funding
43  vout.1: normal marker vout for easy searching
44  vout.2: normal change
45  vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
46  
47  addfunding
48  vins.*: normal inputs
49  vout.0: CC vout for funding
50  vout.1: normal change
51  vout.n-1: opreturn 'A' sbits fundingtxid
52  
53  lock
54  vins.*: normal inputs
55  vout.0: CC vout for locked funds
56  vout.1: normal output to unlock address
57  vout.2: change
58  vout.n-1: opreturn 'L' sbits fundingtxid
59  
60  unlock
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
66  
67  */
68
69 uint64_t RewardsCalc(uint64_t amount,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
70 {
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 )
74     {
75         fprintf(stderr,"duration %llu < minseconds %llu\n",(long long)duration,(long long)minseconds);
76         return(0);
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);
82     return(reward);
83 }
84
85 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
86 {
87     CScript opret; uint8_t evalcode = EVAL_REWARDS;
88     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
89     return(opret);
90 }
91
92 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
93 {
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 )
98     {
99         if ( e == EVAL_REWARDS && f == 'F' )
100             return(f);
101     }
102     return(0);
103 }
104
105 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
106 {
107     CScript opret; uint8_t evalcode = EVAL_REWARDS;
108     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
109     return(opret);
110 }
111
112 uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
113 {
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 )
117     {
118         script = (uint8_t *)vopret.data();
119         if ( script[0] == EVAL_REWARDS )
120         {
121             if ( script[1] == 'F' )
122             {
123                 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
124                 {
125                     fundingtxid = txid;
126                     return('F');
127                 } else fprintf(stderr,"unmarshal error for F\n");
128             }
129             else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
130             {
131                 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
132                     return(f);
133                 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
134             }
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());
137     return(0);
138 }
139
140 uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
141 {
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 )
144     {
145         txid = tx.GetHash();
146         if ( (funcid=  DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
147         {
148             
149             if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
150                 return(tx.vout[v].nValue);
151         }
152     }
153     return(0);
154 }
155
156 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
157 {
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++)
163     {
164         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
165         {
166             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
167                 return eval->Invalid("always should find vin, but didnt");
168             else
169             {
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;
174             }
175         }
176     }
177     for (i=0; i<numvouts; i++)
178     {
179         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
180         if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
181             outputs += assetoshis;
182     }
183     if ( inputs != outputs+txfee )
184     {
185         fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
186         return eval->Invalid("mismatched inputs != outputs + txfee");
187     }
188     else return(true);
189 }
190
191 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
192 {
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;
197     if ( numvouts < 1 )
198         return eval->Invalid("no vouts");
199     else
200     {
201         txid = tx.GetHash();
202         if ( (funcid=  DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
203         {
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");
208             switch ( funcid )
209             {
210                 case 'F':
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");
217                     break;
218                 case 'A':
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");
224                     break;
225                 case 'L':
226                     //vins.*: normal inputs
227                     //vout.0: CC vout for locked funds
228                     //vout.1: normal output to unlock address
229                     //vout.2: change
230                     //vout.n-1: opreturn 'L' sbits fundingtxid
231                     return eval->Invalid("unexpected RewardsValidate for lock");
232                     break;
233                 case 'U':
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++)
240                     {
241                         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
242                             return eval->Invalid("unexpected normal vin for unlock");
243                     }
244                     if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 )
245                         return false;
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");
262                     preventCCvouts = 1;
263                     break;
264             }
265         }
266         return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
267     }
268     return(true);
269 }
270
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)
273 {
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++)
279     {
280         txid = it->first.txhash;
281         vout = (int32_t)it->first.index;
282         if ( it->second.satoshis < 1000000 )
283             continue;
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 )
287                 break;
288         if ( j != mtx.vin.size() )
289             continue;
290         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
291         {
292             if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
293             {
294                 if ( sbits != refsbits || fundingtxid != reffundingtxid )
295                     continue;
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' )
298                     continue;
299                 else if ( fundsflag == 0 && (funcid != 'L' || tx.vout.size() < 4) )
300                     continue;
301                 if ( total != 0 && maxinputs != 0 )
302                 {
303                     if ( fundsflag == 0 )
304                         scriptPubKey = tx.vout[1].scriptPubKey;
305                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
306                 }
307                 totalinputs += it->second.satoshis;
308                 n++;
309                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
310                     break;
311             } else fprintf(stderr,"null funcid\n");
312         }
313     }
314     return(totalinputs);
315 }
316
317 uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
318 {
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++)
324     {
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 )
328         {
329             if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
330             {
331                 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
332                 {
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);
338         }
339     }
340     return(totalinputs);
341 }
342
343 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
344 {
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++)
350     {
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 )
354         {
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' )
357             {
358                 if ( sbits == refsbits )
359                     return(true);
360             }
361         }
362     }
363     return(false);
364 }
365
366 UniValue RewardsInfo(uint256 rewardsid)
367 {
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 )
370     {
371         fprintf(stderr,"cant find fundingtxid\n");
372         result.push_back(Pair("error","cant find fundingtxid"));
373         return(result);
374     }
375     if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
376     {
377         fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
378         result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
379         return(result);
380     }
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));
397     return(result);
398 }
399
400 UniValue RewardsList()
401 {
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++)
406     {
407         txid = it->first.txhash;
408         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
409         {
410             if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
411             {
412                 result.push_back(uint256_str(str,txid));
413             }
414         }
415     }
416     return(result);
417 }
418
419 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
420 {
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 )
423     {
424         fprintf(stderr,"negative parameter error\n");
425         return(0);
426     }
427     cp = CCinit(&C,EVAL_REWARDS);
428     if ( txfee == 0 )
429         txfee = 10000;
430     mypk = pubkey2pk(Mypubkey());
431     rewardspk = GetUnspendable(cp,0);
432     sbits = stringbits(planstr);
433     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
434     {
435         fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
436         return(0);
437     }
438     if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
439     {
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)));
443     }
444     fprintf(stderr,"cant find enough inputs\n");
445     return(0);
446 }
447
448 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
449 {
450     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
451     if ( amount < 0 )
452     {
453         fprintf(stderr,"negative parameter error\n");
454         return(0);
455     }
456     cp = CCinit(&C,EVAL_REWARDS);
457     if ( txfee == 0 )
458         txfee = 10000;
459     mypk = pubkey2pk(Mypubkey());
460     rewardspk = GetUnspendable(cp,0);
461     sbits = stringbits(planstr);
462     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
463     {
464         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
465         return(0);
466     }
467     sbits = stringbits(planstr);
468     if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
469     {
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");
474     return(0);
475 }
476
477 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
478 {
479     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
480     if ( deposit < 0 )
481     {
482         fprintf(stderr,"negative parameter error\n");
483         return(0);
484     }
485     cp = CCinit(&C,EVAL_REWARDS);
486     if ( txfee == 0 )
487         txfee = 10000;
488     mypk = pubkey2pk(Mypubkey());
489     rewardspk = GetUnspendable(cp,0);
490     sbits = stringbits(planstr);
491     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
492     {
493         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
494         return(0);
495     }
496     if ( deposit < mindeposit )
497     {
498         fprintf(stderr,"Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
499         return(0);
500     }
501     if ( (funding= RewardsPlanFunds(sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
502     {
503         if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
504         {
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);
509     }
510     fprintf(stderr,"cant find rewards inputs\n");
511     return(0);
512 }
513
514 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
515 {
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);
518     if ( txfee == 0 )
519         txfee = 10000;
520     rewardspk = GetUnspendable(cp,0);
521     mypk = pubkey2pk(Mypubkey());
522     sbits = stringbits(planstr);
523     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
524     {
525         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
526         return(0);
527     }
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);
531     else
532     {
533         GetCCaddress(cp,coinaddr,rewardspk);
534         if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
535         {
536             fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
537             return(0);
538         }
539         if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
540         {
541             scriptPubKey = tx.vout[1].scriptPubKey;
542             mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
543         }
544         else
545         {
546             fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
547             return(0);
548         }
549     }
550     if ( amount > 0 && (reward= RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit)) > txfee && scriptPubKey.size() > 0 )
551     {
552         if ( (inputs= AddRewardsInputs(ignore,1,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) > 0 )
553         {
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)));
560         }
561         fprintf(stderr,"cant find enough rewards inputs\n");
562     }
563     fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);
564     return(0);
565 }
566
This page took 0.057273 seconds and 4 git commands to generate.