]> Git Repo - VerusCoin.git/blame - src/cc/rewards.cpp
Fix
[VerusCoin.git] / src / cc / rewards.cpp
CommitLineData
194ad5b8 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/*
cfea7a46 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
194ad5b8 38 */
39
db6bfcc4 40uint64_t RewardsCalc(uint64_t claim,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
194ad5b8 41{
42 uint64_t reward = 0;
3ed54ff1 43 // get txtime2, get pblock->nTime
cfea7a46 44 // if elapsed < mintime -> return 0
45 // if elapsed > maxtime, elapsed = maxtime
46 // calc reward
194ad5b8 47 return(reward);
48}
49
88acd162 50CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
6dcd15b5 51{
52 CScript opret; uint8_t evalcode = EVAL_REWARDS;
88acd162 53 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
54 return(opret);
55}
56
57uint8_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 )
6dcd15b5 63 {
88acd162 64 if ( e == EVAL_REWARDS && f == 'F' )
65 return(f);
6dcd15b5 66 }
88acd162 67 return(0);
68}
69
c4e7f616 70CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
88acd162 71{
72 CScript opret; uint8_t evalcode = EVAL_REWARDS;
c4e7f616 73 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
6dcd15b5 74 return(opret);
75}
76
c4e7f616 77uint8_t DecodeRewardsOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
6dcd15b5 78{
88acd162 79 std::vector<uint8_t> vopret; uint8_t *script,e,f;
6dcd15b5 80 GetOpReturnData(scriptPubKey, vopret);
81 script = (uint8_t *)vopret.data();
c4e7f616 82 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
6dcd15b5 83 {
eac2c15e 84 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
88acd162 85 return(f);
6dcd15b5 86 }
88acd162 87 return(0);
6dcd15b5 88}
89
e04b5c08 90uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
194ad5b8 91{
92 char destaddr[64];
93 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
94 {
e04b5c08 95 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
194ad5b8 96 return(tx.vout[v].nValue);
97 }
98 return(0);
99}
100
287efad4 101bool RewardsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
194ad5b8 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 {
287efad4 109 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
194ad5b8 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");
e04b5c08 117 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
194ad5b8 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);
e04b5c08 125 if ( (assetoshis= IsRewardsvout(cp,tx,i)) != 0 )
194ad5b8 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
287efad4 136bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
194ad5b8 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 {
cfea7a46 146 // follow rules
194ad5b8 147 for (i=0; i<numvins; i++)
148 {
149 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
150 return eval->Invalid("illegal normal vini");
151 }
287efad4 152 if ( RewardsExactAmounts(cp,eval,tx,1,10000) == false )
194ad5b8 153 return false;
154 else
155 {
156 preventCCvouts = 1;
e04b5c08 157 if ( IsRewardsvout(cp,tx,0) != 0 )
194ad5b8 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
e04b5c08 170uint64_t AddRewardsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
194ad5b8 171{
4610bb91 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;
194ad5b8 173 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
e04b5c08 174 GetCCaddress(cp,coinaddr,pk);
194ad5b8 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;
fac87764 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 )
194ad5b8 186 {
d18420d2 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 )
194ad5b8 188 {
189 if ( total != 0 && maxinputs != 0 )
fac87764 190 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
cfea7a46 191 totalinputs += it->second.satoshis;
194ad5b8 192 n++;
193 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
194 break;
195 }
196 }
197 }
198 return(totalinputs);
199}
200
fac87764 201uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
cfea7a46 202{
4610bb91 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;
cfea7a46 204 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
cfea7a46 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 {
fac87764 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 )
cfea7a46 212 {
d18420d2 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 )
cfea7a46 214 {
fac87764 215 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
216 {
217 if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout)) > 0 )
218 totalinputs += nValue;
219 }
cfea7a46 220 }
221 }
222 }
223 return(totalinputs);
224}
225
db6bfcc4 226bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
eac2c15e 227{
db6bfcc4 228 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
fac87764 229 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
4610bb91 230 GetCCaddress(cp,CCaddr,rewardspk);
231 SetCCtxids(txids,CCaddr);
fac87764 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;
d18420d2 236 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
fac87764 237 {
d18420d2 238 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
fac87764 239 {
240 if ( sbits == refsbits )
241 return(true);
242 }
243 }
244 }
245 return(false);
eac2c15e 246}
247
c4e7f616 248std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
e04b5c08 249{
db6bfcc4 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;
e04b5c08 251 cp = CCinit(&C,EVAL_REWARDS);
252 if ( txfee == 0 )
253 txfee = 10000;
254 rewardspk = GetUnspendable(cp,0);
255 mypk = pubkey2pk(Mypubkey());
fac87764 256 sbits = stringbits(planstr);
db6bfcc4 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 }
c4e7f616 262 if ( locktxid == zeroid )
db6bfcc4 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 )
e04b5c08 270 {
cfea7a46 271 if ( (inputs= AddRewardsInputs(cp,mtx,mypk,reward+amount+txfee,30)) > 0 )
e04b5c08 272 {
c4e7f616 273 if ( inputs >= (amount + reward + 2*txfee) )
636517f8 274 CCchange = (inputs - (amount + reward + txfee));
e04b5c08 275 if ( CCchange != 0 )
cfea7a46 276 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
277 mtx.vout.push_back(CTxOut(amount+reward,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
c4e7f616 278 return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
e04b5c08 279 }
88acd162 280 fprintf(stderr,"cant find enough rewards inputs\n");
281 }
282 fprintf(stderr,"cant find rewards inputs\n");
e04b5c08 283 return(0);
284}
285
e95b9582 286std::string RewardsCreateFunding(uint64_t txfee,char *planstr,uint64_t funds,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
194ad5b8 287{
db6bfcc4 288 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
e04b5c08 289 cp = CCinit(&C,EVAL_REWARDS);
194ad5b8 290 if ( txfee == 0 )
291 txfee = 10000;
292 mypk = pubkey2pk(Mypubkey());
e04b5c08 293 rewardspk = GetUnspendable(cp,0);
db6bfcc4 294 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
eac2c15e 295 {
296 fprintf(stderr,"Rewards plan %s already exists\n",planstr);
297 return(0);
298 }
88acd162 299 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
194ad5b8 300 {
cfea7a46 301 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
88acd162 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)));
194ad5b8 304 }
88acd162 305 fprintf(stderr,"cant find enough inputs\n");
194ad5b8 306 return(0);
307}
308
e95b9582 309std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t amount)
310{
db6bfcc4 311 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
e95b9582 312 cp = CCinit(&C,EVAL_REWARDS);
313 if ( txfee == 0 )
314 txfee = 10000;
315 mypk = pubkey2pk(Mypubkey());
316 rewardspk = GetUnspendable(cp,0);
db6bfcc4 317 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
e95b9582 318 {
eac2c15e 319 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
320 return(0);
e95b9582 321 }
eac2c15e 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");
e95b9582 328 return(0);
329}
330
1d801ae6 331std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t deposit)
194ad5b8 332{
87ccafbd 333 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
e04b5c08 334 cp = CCinit(&C,EVAL_REWARDS);
85c54375 335 if ( txfee == 0 )
336 txfee = 10000;
194ad5b8 337 mypk = pubkey2pk(Mypubkey());
cfea7a46 338 rewardspk = GetUnspendable(cp,0);
fac87764 339 sbits = stringbits(planstr);
1d801ae6 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
194ad5b8 351 {
1d801ae6 352 if ( AddNormalinputs(mtx,mypk,deposit+txfee,64) > 0 )
cfea7a46 353 {
1d801ae6 354 mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
c4e7f616 355 return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
88acd162 356 } else fprintf(stderr,"cant find enough inputs\n");
cfea7a46 357 }
88acd162 358 fprintf(stderr,"cant find rewards inputs\n");
194ad5b8 359 return(0);
360}
361
194ad5b8 362
This page took 0.074551 seconds and 4 git commands to generate.