]> Git Repo - VerusCoin.git/blob - src/cc/rewards.cpp
Test
[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 uint64_t RewardsCalc(uint64_t claim,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
41 {
42     uint64_t reward = 0;
43     // get start time, get current time
44     // if elapsed < mintime -> return 0
45     // if elapsed > maxtime, elapsed = maxtime
46     // calc reward
47     return(reward);
48 }
49
50 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
51 {
52     CScript opret; uint8_t evalcode = EVAL_REWARDS;
53     opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
54     return(opret);
55 }
56
57 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
58 {
59     std::vector<uint8_t> vopret; uint8_t *script,e,f;
60     GetOpReturnData(scriptPubKey, vopret);
61     script = (uint8_t *)vopret.data();
62     if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
63     {
64         if ( e == EVAL_REWARDS && f == 'F' )
65             return(f);
66     }
67     return(0);
68 }
69
70 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
71 {
72     CScript opret; uint8_t evalcode = EVAL_REWARDS;
73     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
74     return(opret);
75 }
76
77 uint8_t DecodeRewardsOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
78 {
79     std::vector<uint8_t> vopret; uint8_t *script,e,f;
80     GetOpReturnData(scriptPubKey, vopret);
81     script = (uint8_t *)vopret.data();
82     if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
83     {
84         if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
85             return(f);
86     }
87     return(0);
88 }
89
90 uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
91 {
92     char destaddr[64];
93     if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
94     {
95         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
96             return(tx.vout[v].nValue);
97     }
98     return(0);
99 }
100
101 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
102 {
103     static uint256 zerohash;
104     CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
105     numvins = tx.vin.size();
106     numvouts = tx.vout.size();
107     for (i=0; i<numvins; i++)
108     {
109         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
110         {
111             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
112                 return eval->Invalid("always should find vin, but didnt");
113             else
114             {
115                 if ( hashBlock == zerohash )
116                     return eval->Invalid("cant rewards from mempool");
117                 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
118                     inputs += assetoshis;
119             }
120         }
121     }
122     for (i=0; i<numvouts; i++)
123     {
124         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
125         if ( (assetoshis= IsRewardsvout(cp,tx,i)) != 0 )
126             outputs += assetoshis;
127     }
128     if ( inputs != outputs+COIN+txfee )
129     {
130         fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
131         return eval->Invalid("mismatched inputs != outputs + COIN + txfee");
132     }
133     else return(true);
134 }
135
136 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
137 {
138     int32_t numvins,numvouts,preventCCvins,preventCCvouts,i;
139     numvins = tx.vin.size();
140     numvouts = tx.vout.size();
141     preventCCvins = preventCCvouts = -1;
142     if ( numvouts < 1 )
143         return eval->Invalid("no vouts");
144     else
145     {
146         // follow rules
147         for (i=0; i<numvins; i++)
148         {
149             if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
150                 return eval->Invalid("illegal normal vini");
151         }
152         if ( RewardsExactAmounts(cp,eval,tx,1,10000) == false )
153             return false;
154         else
155         {
156             preventCCvouts = 1;
157             if ( IsRewardsvout(cp,tx,0) != 0 )
158             {
159                 preventCCvouts++;
160                 i = 1;
161             } else i = 0;
162             if ( tx.vout[i].nValue != COIN )
163                 return eval->Invalid("invalid rewards output");
164             return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
165         }
166     }
167     return(true);
168 }
169
170 uint64_t AddRewardsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
171 {
172     char coinaddr[64]; uint64_t sbits,APR,minseconds,maxseconds,mindeposit,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid;
173     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
174     GetCCaddress(cp,coinaddr,pk);
175     SetCCunspents(unspentOutputs,coinaddr);
176     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
177     {
178         txid = it->first.txhash;
179         vout = (int32_t)it->first.index;
180         for (j=0; j<mtx.vin.size(); j++)
181             if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
182                 break;
183         if ( j != mtx.vin.size() )
184             continue;
185         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
186         {
187             if ( (funcid= DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit)) == 'F' || DecodeRewardsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid) != 0 )
188             {
189                 if ( total != 0 && maxinputs != 0 )
190                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
191                 totalinputs += it->second.satoshis;
192                 n++;
193                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
194                     break;
195             }
196         }
197     }
198     return(totalinputs);
199 }
200
201 uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
202 {
203     char coinaddr[64]; uint64_t sbits,APR,minseconds,maxseconds,mindeposit,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
204     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
205     GetCCaddress(cp,coinaddr,pk);
206     SetCCunspents(unspentOutputs,coinaddr);
207     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
208     {
209         txid = it->first.txhash;
210         vout = (int32_t)it->first.index;
211         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
212         {
213             if ( (funcid= DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit)) == 'F' || DecodeRewardsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid) != 0 )
214             {
215                 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
216                 {
217                     if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout)) > 0 )
218                         totalinputs += nValue;
219                 }
220             }
221         }
222     }
223     return(totalinputs);
224 }
225
226 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
227 {
228     char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
229     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
230     GetCCaddress(cp,CCaddr,rewardspk);
231     SetCCtxids(txids,CCaddr);
232     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
233     {
234         //int height = it->first.blockHeight;
235         txid = it->first.txhash;
236         if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
237         {
238             if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
239             {
240                 if ( sbits == refsbits )
241                     return(true);
242             }
243         }
244     }
245     return(false);
246 }
247
248 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
249 {
250     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t funding,sbits,reward,amount=0,inputs,CCchange=0,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
251     cp = CCinit(&C,EVAL_REWARDS);
252     if ( txfee == 0 )
253         txfee = 10000;
254     rewardspk = GetUnspendable(cp,0);
255     mypk = pubkey2pk(Mypubkey());
256     sbits = stringbits(planstr);
257     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
258     {
259         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
260         return(0);
261     }
262     if ( locktxid == zeroid )
263         amount = AddRewardsInputs(cp,mtx,rewardspk,(1LL << 30),1);
264     else
265     {
266         fprintf(stderr,"check if locktxid is unspent\n");
267         return(0);
268     }
269     if ( amount > 0 && (reward= RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit)) > txfee )
270     {
271         if ( (inputs= AddRewardsInputs(cp,mtx,mypk,reward+amount+txfee,30)) > 0 )
272         {
273             if ( inputs >= (amount + reward + 2*txfee) )
274                 CCchange = (inputs - amount - reward - txfee);
275             if ( CCchange != 0 )
276                 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
277             mtx.vout.push_back(CTxOut(amount+reward,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
278             return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
279         }
280         fprintf(stderr,"cant find enough rewards inputs\n");
281     }
282     fprintf(stderr,"cant find rewards inputs\n");
283     return(0);
284 }
285
286 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,uint64_t funds,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
287 {
288     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
289     cp = CCinit(&C,EVAL_REWARDS);
290     if ( txfee == 0 )
291         txfee = 10000;
292     mypk = pubkey2pk(Mypubkey());
293     rewardspk = GetUnspendable(cp,0);
294     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
295     {
296         fprintf(stderr,"Rewards plan %s already exists\n",planstr);
297         return(0);
298     }
299     if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
300     {
301         mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
302         mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
303         return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
304     }
305     fprintf(stderr,"cant find enough inputs\n");
306     return(0);
307 }
308
309 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t amount)
310 {
311     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
312     cp = CCinit(&C,EVAL_REWARDS);
313     if ( txfee == 0 )
314         txfee = 10000;
315     mypk = pubkey2pk(Mypubkey());
316     rewardspk = GetUnspendable(cp,0);
317     if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
318     {
319         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
320         return(0);
321     }
322     if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
323     {
324         mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
325         return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
326     } else fprintf(stderr,"cant find enough inputs\n");
327     fprintf(stderr,"cant find fundingtxid\n");
328     return(0);
329 }
330
331 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t deposit)
332 {
333     CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
334     cp = CCinit(&C,EVAL_REWARDS);
335     if ( txfee == 0 )
336         txfee = 10000;
337     mypk = pubkey2pk(Mypubkey());
338     rewardspk = GetUnspendable(cp,0);
339     sbits = stringbits(planstr);
340     if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
341     {
342         fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
343         return(0);
344     }
345     if ( deposit < mindeposit )
346     {
347         fprintf(stderr,"Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
348         return(0);
349     }
350     if ( (funding= RewardsPlanFunds(sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
351     {
352         if ( AddNormalinputs(mtx,mypk,deposit+txfee,64) > 0 )
353         {
354             mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
355             return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
356         } else fprintf(stderr,"cant find enough inputs\n");
357     }
358     fprintf(stderr,"cant find rewards inputs\n");
359     return(0);
360 }
361
362
This page took 0.045495 seconds and 4 git commands to generate.