1 /******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
4 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
5 * the top-level directory of this distribution for the individual copyright *
6 * holder information and the developer policies on copyright and licensing. *
8 * Unless otherwise agreed in a custom licensing agreement, no part of the *
9 * SuperNET software, including this file may be copied, modified, propagated *
10 * or distributed except according to the terms contained in the LICENSE file *
12 * Removal or modification of this copyright notice is prohibited. *
14 ******************************************************************************/
16 #include "CCinclude.h"
19 The rewards CC contract is initially for OOT, which needs this functionality. However, many of the attributes can be parameterized to allow different rewards programs to run. Multiple rewards plans could even run on the same blockchain, though the user would need to choose which one to lock funds into.
21 At the high level, the user would lock funds for some amount of time and at the end of it, would get all the locked funds back with an additional reward. So there needs to be a lock funds and unlock funds ability. Additionally, the rewards need to come from somewhere, so similar to the faucet, there would be a way to fund the reward.
23 Additional requirements are for the user to be able to lock funds via SPV. This requirement in turns forces the creation of a way for anybody to be able to unlock the funds as that operation requires a native daemon running and cant be done over SPV. The idea is to allow anybody to run a script that would unlock all funds that are matured. As far as the user is concerned, he locks his funds via SPV and after some time it comes back with an extra reward.
25 In reality, the funds are locked into a CC address that is unspendable, except for some special conditions and it needs to come back to the address that funded the lock. In order to implement this, several things are clear.
27 1) each locked CC utxo needs to be linked to a specific rewards plan
28 2) each locked CC utxo needs to know the only address that it can be unlocked into
29 3) SPV requirement means the lock transaction needs to be able to be created without any CC signing
31 The opreturn will be used to store the name of the rewards plan and all funding and locked funds with the same plan will use the same pool of rewards. plan names will be limited to 8 chars and encoded into a uint64_t.
33 The initial funding transaction will have all the parameters for the rewards plan encoded in the vouts. Additional fundings will just increase the available CC utxo for the rewards.
35 Locks wont have any CC vins, but will send to the RewardsCCaddress, with the plan stringbits in the opreturn. vout1 will have the unlock address and no other destination is valid.
37 Unlock does a CC spend to the vout1 address
40 uint64_t RewardsCalc(uint64_t claim,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
43 // get start time, get current time
44 // if elapsed < mintime -> return 0
45 // if elapsed > maxtime, elapsed = maxtime
50 CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
52 CScript opret; uint8_t evalcode = EVAL_REWARDS;
53 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
57 uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
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 )
64 if ( e == EVAL_REWARDS && f == 'F' )
70 CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
72 CScript opret; uint8_t evalcode = EVAL_REWARDS;
73 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
77 uint8_t DecodeRewardsOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
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 )
84 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
90 uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
93 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
95 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
96 return(tx.vout[v].nValue);
101 bool RewardsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
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++)
109 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
111 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
112 return eval->Invalid("always should find vin, but didnt");
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;
122 for (i=0; i<numvouts; i++)
124 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
125 if ( (assetoshis= IsRewardsvout(cp,tx,i)) != 0 )
126 outputs += assetoshis;
128 if ( inputs != outputs+COIN+txfee )
130 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
131 return eval->Invalid("mismatched inputs != outputs + COIN + txfee");
136 bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
138 int32_t numvins,numvouts,preventCCvins,preventCCvouts,i;
139 numvins = tx.vin.size();
140 numvouts = tx.vout.size();
141 preventCCvins = preventCCvouts = -1;
143 return eval->Invalid("no vouts");
147 for (i=0; i<numvins; i++)
149 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
150 return eval->Invalid("illegal normal vini");
152 if ( RewardsExactAmounts(cp,eval,tx,1,10000) == false )
157 if ( IsRewardsvout(cp,tx,0) != 0 )
162 if ( tx.vout[i].nValue != COIN )
163 return eval->Invalid("invalid rewards output");
164 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
170 uint64_t AddRewardsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
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++)
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 )
183 if ( j != mtx.vin.size() )
185 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
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 )
189 if ( total != 0 && maxinputs != 0 )
190 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
191 totalinputs += it->second.satoshis;
193 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
201 uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
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++)
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 )
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 )
215 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
217 if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout)) > 0 )
218 totalinputs += nValue;
226 bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
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++)
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 )
238 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
240 if ( sbits == refsbits )
248 std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
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);
254 rewardspk = GetUnspendable(cp,0);
255 mypk = pubkey2pk(Mypubkey());
256 sbits = stringbits(planstr);
257 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
259 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
262 if ( locktxid == zeroid )
263 amount = AddRewardsInputs(cp,mtx,rewardspk,(1LL << 30),1);
266 fprintf(stderr,"check if locktxid is unspent\n");
269 if ( amount > 0 && (reward= RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit)) > txfee )
271 if ( (inputs= AddRewardsInputs(cp,mtx,mypk,reward+amount+txfee,30)) > 0 )
273 if ( inputs >= (amount + reward + 2*txfee) )
274 CCchange = (inputs - amount - reward - txfee);
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)));
280 fprintf(stderr,"cant find enough rewards inputs\n");
282 fprintf(stderr,"cant find rewards inputs\n");
286 std::string RewardsCreateFunding(uint64_t txfee,char *planstr,uint64_t funds,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
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);
292 mypk = pubkey2pk(Mypubkey());
293 rewardspk = GetUnspendable(cp,0);
294 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
296 fprintf(stderr,"Rewards plan %s already exists\n",planstr);
299 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
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)));
305 fprintf(stderr,"cant find enough inputs\n");
309 std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t amount)
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);
315 mypk = pubkey2pk(Mypubkey());
316 rewardspk = GetUnspendable(cp,0);
317 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
319 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
322 if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
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");
331 std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint64_t deposit)
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);
337 mypk = pubkey2pk(Mypubkey());
338 rewardspk = GetUnspendable(cp,0);
339 sbits = stringbits(planstr);
340 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
342 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
345 if ( deposit < mindeposit )
347 fprintf(stderr,"Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
350 if ( (funding= RewardsPlanFunds(sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
352 if ( AddNormalinputs(mtx,mypk,deposit+txfee,64) > 0 )
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");
358 fprintf(stderr,"cant find rewards inputs\n");