]> Git Repo - VerusCoin.git/blob - src/cc/rewards.cpp
Merge branch 'dev' of github.com:miketout/VerusCoin into dev
[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 "CCrewards.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 int64_t RewardsCalc(int64_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     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 )
84         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);
86     return(reward);
87 }
88
89 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
90 {
91     CScript opret; uint8_t evalcode = EVAL_REWARDS;
92     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
93     return(opret);
94 }
95
96 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
97 {
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 )
102     {
103         if ( e == EVAL_REWARDS && f == 'F' )
104             return(f);
105     }
106     return(0);
107 }
108
109 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
110 {
111     CScript opret; uint8_t evalcode = EVAL_REWARDS;
112     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
113     return(opret);
114 }
115
116 uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
117 {
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 )
121     {
122         script = (uint8_t *)vopret.data();
123         if ( script[0] == EVAL_REWARDS )
124         {
125             if ( script[1] == 'F' )
126             {
127                 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
128                 {
129                     fundingtxid = txid;
130                     return('F');
131                 } else fprintf(stderr,"unmarshal error for F\n");
132             }
133             else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
134             {
135                 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
136                     return(f);
137                 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
138             }
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());
141     return(0);
142 }
143
144 int64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
145 {
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 )
148     {
149         txid = tx.GetHash();
150         if ( (funcid=  DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
151         {
152             
153             if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
154                 return(tx.vout[v].nValue);
155         }
156     }
157     return(0);
158 }
159
160 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
161 {
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++)
167     {
168         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
169         {
170             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
171                 return eval->Invalid("always should find vin, but didnt");
172             else
173             {
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;
178             }
179         }
180     }
181     for (i=0; i<numvouts; i++)
182     {
183         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
184         if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
185             outputs += assetoshis;
186     }
187     if ( inputs != outputs+txfee )
188     {
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");
191     }
192     else return(true);
193 }
194
195 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn, bool fulfilled)
196 {
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;
201     if ( numvouts < 1 )
202         return eval->Invalid("no vouts");
203     else
204     {
205         txid = tx.GetHash();
206         if ( (funcid=  DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
207         {
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");
214             switch ( funcid )
215             {
216                 case 'F':
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");
223                     break;
224                 case 'A':
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");
230                     break;
231                 case 'L':
232                     //vins.*: normal inputs
233                     //vout.0: CC vout for locked funds
234                     //vout.1: normal output to unlock address
235                     //vout.2: change
236                     //vout.n-1: opreturn 'L' sbits fundingtxid
237                     return eval->Invalid("unexpected RewardsValidate for lock");
238                     break;
239                 case 'U':
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++)
254                     {
255                         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
256                             return eval->Invalid("unexpected normal vin for unlock");
257                     }
258                     if ( numvouts == 2 && numvins == 1 )
259                     {
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");
268                         else return(true);
269                     }
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 )
283                         return false;
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");
288                     preventCCvouts = 1;
289                     break;
290             }
291         }
292         return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
293     }
294     return(true);
295 }
296
297 static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t refsbits,uint256 reffundingtxid,uint64_t needed)
298 {
299     uint8_t funcid; uint64_t sbits,nValue; uint256 fundingtxid; char str[65];
300     memset(&txid,0,sizeof(txid));
301     vout = -1;
302     nValue = 0;
303     BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
304     {
305         const CTransaction &tx = e.GetTx();
306         if ( tx.vout.size() > 0 && tx.vout[0].nValue >= needed )
307         {
308             const uint256 &hash = tx.GetHash();
309             if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(hash,0) == 0 )
310             {
311                 if ( (funcid= DecodeRewardsOpRet(hash,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'U' && sbits == refsbits && fundingtxid == reffundingtxid )
312                 {
313                     txid = hash;
314                     vout = 0;
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);
317                     return(nValue);
318                 }
319             }
320         }
321     }
322     return(nValue);
323 }
324
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)
327 {
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++)
333     {
334         txid = it->first.txhash;
335         vout = (int32_t)it->first.index;
336         if ( it->second.satoshis < 1000000 )
337             continue;
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 )
341                 break;
342         if ( j != mtx.vin.size() )
343             continue;
344         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
345         {
346             if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
347             {
348                 if ( sbits != refsbits || fundingtxid != reffundingtxid )
349                     continue;
350                 if ( maxseconds == 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
351                     continue;
352                 else if ( maxseconds != 0 && funcid != 'L' )
353                 {
354                     if ( CCduration(numblocks,txid) < maxseconds )
355                         continue;
356                 }
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 )
359                 {
360                     if ( maxseconds != 0 )
361                         scriptPubKey = tx.vout[1].scriptPubKey;
362                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
363                 }
364                 totalinputs += it->second.satoshis;
365                 n++;
366                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
367                     break;
368             } else fprintf(stderr,"null funcid\n");
369         }
370     }
371     if ( maxseconds == 0 && totalinputs < total && (maxinputs == 0 || n < maxinputs-1) )
372     {
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 )
375         {
376             mtx.vin.push_back(CTxIn(txid,vout,CScript()));
377             fprintf(stderr,"added mempool vout for %.8f\n",(double)nValue/COIN);
378             totalinputs += nValue;
379             n++;
380         }
381     }
382     return(totalinputs);
383 }
384
385 int64_t RewardsPlanFunds(uint64_t &lockedfunds,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
386 {
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;
389     lockedfunds = 0;
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++)
393     {
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 )
397         {
398             if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'A' || funcid == 'U' || funcid == 'L' )
399             {
400                 if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
401                 {
402                     if ( (nValue= IsRewardsvout(cp,tx,vout,sbits,fundingtxid)) > 0 )
403                     {
404                         if ( funcid == 'L' )
405                             lockedfunds += nValue;
406                         else totalinputs += nValue;
407                     }
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);
411         }
412     }
413     return(totalinputs);
414 }
415
416 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
417 {
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++)
423     {
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 )
427         {
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' )
430             {
431                 if ( sbits == refsbits )
432                     return(true);
433             }
434         }
435     }
436     return(false);
437 }
438
439 UniValue RewardsInfo(uint256 rewardsid)
440 {
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 )
443     {
444         fprintf(stderr,"cant find fundingtxid\n");
445         result.push_back(Pair("result","error"));
446         result.push_back(Pair("error","cant find fundingtxid"));
447         return(result);
448     }
449     if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
450     {
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"));
454         return(result);
455     }
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));
474     return(result);
475 }
476
477 UniValue RewardsList()
478 {
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++)
483     {
484         txid = it->first.txhash;
485         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
486         {
487             if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
488             {
489                 result.push_back(uint256_str(str,txid));
490             }
491         }
492     }
493     return(result);
494 }
495
496 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
497 {
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 )
500     {
501         fprintf(stderr,"negative parameter error\n");
502         return("");
503     }
504     if ( APR > REWARDSCC_MAXAPR )
505     {
506         fprintf(stderr,"25%% APR is maximum\n");
507         return("");
508     }
509     cp = CCinit(&C,EVAL_REWARDS);
510     if ( txfee == 0 )
511         txfee = 10000;
512     mypk = pubkey2pk(Mypubkey());
513     rewardspk = GetUnspendable(cp,0);
514     sbits = stringbits(planstr);
515     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
516     {
517         fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
518         return("");
519     }
520     if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
521     {
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)));
525     }
526     fprintf(stderr,"cant find enough inputs\n");
527     return("");
528 }
529
530 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
531 {
532     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
533     if ( amount < 0 )
534     {
535         fprintf(stderr,"negative parameter error\n");
536         return("");
537     }
538     cp = CCinit(&C,EVAL_REWARDS);
539     if ( txfee == 0 )
540         txfee = 10000;
541     mypk = pubkey2pk(Mypubkey());
542     rewardspk = GetUnspendable(cp,0);
543     sbits = stringbits(planstr);
544     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
545     {
546         CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
547         fprintf(stderr,"%s\n",CCerror.c_str());
548         return("");
549     }
550     sbits = stringbits(planstr);
551     if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
552     {
553         mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
554         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
555     } else {
556         CCerror = "cant find enough inputs";
557         fprintf(stderr,"%s\n", CCerror.c_str());
558     }
559     CCerror = "cant find fundingtxid";
560     fprintf(stderr,"%s\n", CCerror.c_str());
561     return("");
562 }
563
564 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
565 {
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 )
568     {
569         CCerror = "deposit amount less than txfee";
570         fprintf(stderr,"%s\n",CCerror.c_str());
571         return("");
572     }
573     cp = CCinit(&C,EVAL_REWARDS);
574     if ( txfee == 0 )
575         txfee = 10000;
576     mypk = pubkey2pk(Mypubkey());
577     rewardspk = GetUnspendable(cp,0);
578     sbits = stringbits(planstr);
579     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
580     {
581         CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
582         fprintf(stderr,"%s\n",CCerror.c_str());
583         return("");
584     }
585     if ( deposit < mindeposit )
586     {
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());
589         return("");
590     }
591     if ( (funding= RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
592     {
593         if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
594         {
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)));
598         } else {
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());
601         }
602     }
603     fprintf(stderr,"cant find rewards inputs funding %.8f locked %.8f vs deposit %.8f\n",(double)funding/COIN,(double)lockedfunds/COIN,(double)deposit/COIN);
604     return("");
605 }
606
607 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
608 {
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);
611     if ( txfee == 0 )
612         txfee = 10000;
613     rewardspk = GetUnspendable(cp,0);
614     mypk = pubkey2pk(Mypubkey());
615     sbits = stringbits(planstr);
616     if ( locktxid == fundingtxid )
617     {
618         fprintf(stderr,"Rewards plan cant unlock fundingtxid\n");
619         CCerror = "Rewards plan cant unlock fundingtxid";
620         return("");
621     }
622     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
623     {
624         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
625         CCerror = "Rewards plan does not exist";
626         return("");
627     }
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);
631     else
632     {
633         GetCCaddress(cp,coinaddr,rewardspk);
634         if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
635         {
636             fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
637             CCerror = "locktxid/v0 is spent";
638             return("");
639         }
640         if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
641         {
642             scriptPubKey = tx.vout[1].scriptPubKey;
643             mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
644         }
645         else
646         {
647             fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
648             CCerror = "no normal vout.1 in locktxid";
649             return("");
650         }
651     }
652     if ( amount > txfee )
653     {
654         reward = RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
655         if ( scriptPubKey.size() > 0 )
656         {
657             if ( reward > txfee )
658             {
659                 firstmtx = mtx;
660                 if ( (inputs= AddRewardsInputs(ignore,0,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) >= reward+txfee )
661                 {
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)));
668                 }
669                 else
670                 {
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)));
675                 }
676             }
677             else
678             {
679                 CCerror = strprintf("reward %.8f is <= the transaction fee", reward);
680                 fprintf(stderr,"%s\n", CCerror.c_str());
681             }
682         }
683         else
684         {
685             CCerror = "invalid scriptPubKey";
686             fprintf(stderr,"%s\n", CCerror.c_str());
687         }
688     }
689     else
690     {
691         CCerror = "amount must be more than txfee";
692         fprintf(stderr,"%s\n", CCerror.c_str());
693     }
694     fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);
695     return("");
696 }
697
This page took 0.064849 seconds and 4 git commands to generate.