]> Git Repo - VerusCoin.git/blame - src/cc/rewards.cpp
Test
[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
d5dae053 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
194ad5b8 67 */
68
f66eff04 69uint64_t RewardsCalc(uint64_t amount,uint256 txid,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
194ad5b8 70{
f66eff04 71 uint64_t duration,reward = 0;
72 if ( (duration= CCduration(txid)) < minseconds )
626f89e2 73 {
74 //return(0);
2e2244a2 75 duration = (uint32_t)time(NULL) - (1532713903 - 3600 * 24);
59f69770 76 } else if ( duration > maxseconds )
f66eff04 77 maxseconds = duration;
9f92a697 78 reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100);
d40b8630 79 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);
194ad5b8 80 return(reward);
81}
82
88acd162 83CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
6dcd15b5 84{
85 CScript opret; uint8_t evalcode = EVAL_REWARDS;
88acd162 86 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
87 return(opret);
88}
89
90uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
91{
92 std::vector<uint8_t> vopret; uint8_t *script,e,f;
93 GetOpReturnData(scriptPubKey, vopret);
94 script = (uint8_t *)vopret.data();
0af767e0 95 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
6dcd15b5 96 {
88acd162 97 if ( e == EVAL_REWARDS && f == 'F' )
98 return(f);
6dcd15b5 99 }
88acd162 100 return(0);
101}
102
c4e7f616 103CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
88acd162 104{
105 CScript opret; uint8_t evalcode = EVAL_REWARDS;
c4e7f616 106 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
6dcd15b5 107 return(opret);
108}
109
0af767e0 110uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
6dcd15b5 111{
9f2d38b3 112 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; uint64_t APR,minseconds,maxseconds,mindeposit;
6dcd15b5 113 GetOpReturnData(scriptPubKey, vopret);
0af767e0 114 if ( vopret.size() > 2 )
6dcd15b5 115 {
0af767e0 116 script = (uint8_t *)vopret.data();
117 if ( script[0] == EVAL_REWARDS )
118 {
119 if ( script[1] == 'F' )
120 {
cb35ec92 121 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
0af767e0 122 fundingtxid = txid;
cb35ec92 123 else fprintf(stderr,"unmarshal error for F\n");
0af767e0 124 }
125 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
126 {
127 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
128 return(f);
cb35ec92 129 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
0af767e0 130 }
cb35ec92 131 } else fprintf(stderr,"script[0] %02x != EVAL_REWARDS\n",script[0]);
5641892d 132 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
88acd162 133 return(0);
6dcd15b5 134}
135
e04b5c08 136uint64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
194ad5b8 137{
138 char destaddr[64];
139 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
140 {
e04b5c08 141 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
194ad5b8 142 return(tx.vout[v].nValue);
143 }
144 return(0);
145}
146
0af767e0 147bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee)
194ad5b8 148{
149 static uint256 zerohash;
0af767e0 150 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis;
194ad5b8 151 numvins = tx.vin.size();
152 numvouts = tx.vout.size();
153 for (i=0; i<numvins; i++)
154 {
287efad4 155 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
194ad5b8 156 {
157 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
158 return eval->Invalid("always should find vin, but didnt");
159 else
160 {
161 if ( hashBlock == zerohash )
162 return eval->Invalid("cant rewards from mempool");
e04b5c08 163 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
194ad5b8 164 inputs += assetoshis;
165 }
166 }
167 }
168 for (i=0; i<numvouts; i++)
169 {
170 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
e04b5c08 171 if ( (assetoshis= IsRewardsvout(cp,tx,i)) != 0 )
194ad5b8 172 outputs += assetoshis;
173 }
0af767e0 174 if ( inputs != outputs+txfee )
194ad5b8 175 {
176 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
0af767e0 177 return eval->Invalid("mismatched inputs != outputs + txfee");
194ad5b8 178 }
179 else return(true);
180}
181
287efad4 182bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
194ad5b8 183{
9f2d38b3 184 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;
194ad5b8 185 numvins = tx.vin.size();
186 numvouts = tx.vout.size();
187 preventCCvins = preventCCvouts = -1;
188 if ( numvouts < 1 )
189 return eval->Invalid("no vouts");
190 else
191 {
0af767e0 192 txid = tx.GetHash();
193 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
194ad5b8 194 {
0af767e0 195 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
196 return eval->Invalid("cant find fundingtxid");
197 else if ( fundingTx.vout.size() > 0 && DecodeRewardsFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 'F' )
198 return eval->Invalid("fundingTx not valid");
199 switch ( funcid )
194ad5b8 200 {
0af767e0 201 case 'F':
202 //vins.*: normal inputs
203 //vout.0: CC vout for funding
204 //vout.1: normal marker vout for easy searching
205 //vout.2: normal change
206 //vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
207 return eval->Invalid("unexpected RewardsValidate for createfunding");
208 break;
209 case 'A':
210 //vins.*: normal inputs
211 //vout.0: CC vout for funding
212 //vout.1: normal change
213 //vout.n-1: opreturn 'A' sbits fundingtxid
214 return eval->Invalid("unexpected RewardsValidate for addfunding");
215 break;
216 case 'L':
217 //vins.*: normal inputs
218 //vout.0: CC vout for locked funds
219 //vout.1: normal output to unlock address
220 //vout.2: change
221 //vout.n-1: opreturn 'L' sbits fundingtxid
222 return eval->Invalid("unexpected RewardsValidate for lock");
223 break;
224 case 'U':
225 //vin.0: locked funds CC vout.0 from lock
226 //vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
227 //vout.0: funding CC change
228 //vout.1: normal output to unlock address
229 //vout.n-1: opreturn 'U' sbits fundingtxid
230 for (i=0; i<numvins; i++)
231 {
232 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
233 return eval->Invalid("unexpected normal vin for unlock");
234 }
235 if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue) == 0 )
236 return false;
237 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
238 return eval->Invalid("always should find vin.0, but didnt");
239 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
240 return eval->Invalid("lock tx vout.0 is normal output");
241 else if ( tx.vout.size() < 3 )
242 return eval->Invalid("unlock tx not enough vouts");
243 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
244 return eval->Invalid("unlock tx vout.0 is normal output");
9f2d38b3 245 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
0af767e0 246 return eval->Invalid("unlock tx vout.1 is CC output");
247 amount = vinTx.vout[0].nValue;
248 reward = RewardsCalc(amount,txid,APR,minseconds,maxseconds,mindeposit);
249 if ( tx.vout[1].nValue > amount+reward )
250 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
251 preventCCvouts = 1;
252 break;
253 }
194ad5b8 254 }
0af767e0 255 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
194ad5b8 256 }
257 return(true);
258}
259
791cf78c 260// 'L' vs 'F' and 'A'
d5dae053 261uint64_t AddRewardsInputs(CScript &scriptPubKey,int32_t fundsflag,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs)
194ad5b8 262{
1cbb6e13 263 char coinaddr[64],str[65]; 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 264 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
e04b5c08 265 GetCCaddress(cp,coinaddr,pk);
194ad5b8 266 SetCCunspents(unspentOutputs,coinaddr);
267 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
268 {
269 txid = it->first.txhash;
fac87764 270 vout = (int32_t)it->first.index;
1cbb6e13 271 fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
fac87764 272 for (j=0; j<mtx.vin.size(); j++)
273 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
274 break;
275 if ( j != mtx.vin.size() )
276 continue;
feb9f834 277 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
194ad5b8 278 {
0af767e0 279 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
194ad5b8 280 {
242ba739 281 fprintf(stderr,"fundsflag.%d (%c) %.8f %.8f\n",fundsflag,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
d5dae053 282 if ( fundsflag != 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
791cf78c 283 continue;
337c2501 284 else if ( fundsflag == 0 && (funcid != 'L' || tx.vout.size() < 4) )
791cf78c 285 continue;
194ad5b8 286 if ( total != 0 && maxinputs != 0 )
d5dae053 287 {
288 if ( fundsflag == 0 )
289 scriptPubKey = tx.vout[1].scriptPubKey;
fac87764 290 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
d5dae053 291 }
cfea7a46 292 totalinputs += it->second.satoshis;
194ad5b8 293 n++;
294 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
295 break;
93637d45 296 } else fprintf(stderr,"null funcid\n");
194ad5b8 297 }
298 }
299 return(totalinputs);
300}
301
fac87764 302uint64_t RewardsPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
cfea7a46 303{
4610bb91 304 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 305 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
cfea7a46 306 GetCCaddress(cp,coinaddr,pk);
307 SetCCunspents(unspentOutputs,coinaddr);
308 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
309 {
fac87764 310 txid = it->first.txhash;
311 vout = (int32_t)it->first.index;
312 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
cfea7a46 313 {
0af767e0 314 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
cfea7a46 315 {
fac87764 316 if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
317 {
318 if ( refsbits == sbits && (nValue= IsRewardsvout(cp,tx,vout)) > 0 )
319 totalinputs += nValue;
2205fd65 320 else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
e90ab9d5 321 } else fprintf(stderr,"else case\n");
64d610d4 322 } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
cfea7a46 323 }
324 }
325 return(totalinputs);
326}
327
db6bfcc4 328bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
eac2c15e 329{
db6bfcc4 330 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
fac87764 331 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
4610bb91 332 GetCCaddress(cp,CCaddr,rewardspk);
333 SetCCtxids(txids,CCaddr);
fac87764 334 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
335 {
336 //int height = it->first.blockHeight;
337 txid = it->first.txhash;
d18420d2 338 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
fac87764 339 {
d18420d2 340 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
fac87764 341 {
342 if ( sbits == refsbits )
343 return(true);
344 }
345 }
346 }
347 return(false);
eac2c15e 348}
349
fdd22810 350UniValue RewardsInfo(uint256 rewardsid)
351{
46bbc433 352 UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; uint64_t APR,minseconds,maxseconds,mindeposit,sbits; char str[67],numstr[65];
fdd22810 353 if ( GetTransaction(rewardsid,vintx,hashBlock,false) == 0 )
354 {
bbf9b82f 355 fprintf(stderr,"cant find fundingtxid\n");
356 result.push_back(Pair("error","cant find fundingtxid"));
357 return(result);
fdd22810 358 }
bbf9b82f 359 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
fdd22810 360 {
bbf9b82f 361 fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
362 result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
363 return(result);
fdd22810 364 }
365 result.push_back(Pair("result","success"));
366 result.push_back(Pair("fundingtxid",uint256_str(str,rewardsid)));
34589c80 367 unstringbits(str,sbits);
368 result.push_back(Pair("name",str));
d4e61763 369 result.push_back(Pair("sbits",sbits));
fdd22810 370 sprintf(numstr,"%.8f",(double)APR/COIN);
371 result.push_back(Pair("APR",numstr));
372 result.push_back(Pair("minseconds",minseconds));
373 result.push_back(Pair("maxseconds",maxseconds));
374 sprintf(numstr,"%.8f",(double)mindeposit/COIN);
375 result.push_back(Pair("mindeposit",numstr));
376 sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue/COIN);
377 result.push_back(Pair("funding",numstr));
378 return(result);
379}
380
381UniValue RewardsList()
382{
9fa1bcd2 383 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];
fdd22810 384 cp = CCinit(&C,EVAL_REWARDS);
385 SetCCtxids(addressIndex,cp->normaladdr);
386 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
387 {
388 txid = it->first.txhash;
389 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
390 {
9fa1bcd2 391 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
fdd22810 392 {
393 result.push_back(uint256_str(str,txid));
394 }
395 }
396 }
397 return(result);
398}
399
8eee6bd8 400std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
194ad5b8 401{
db6bfcc4 402 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
46bbc433 403 if ( funds < 0 || mindeposit < 0 || minseconds < 0 || maxseconds < 0 )
404 {
405 fprintf(stderr,"negative parameter error\n");
406 return(0);
407 }
e04b5c08 408 cp = CCinit(&C,EVAL_REWARDS);
194ad5b8 409 if ( txfee == 0 )
410 txfee = 10000;
411 mypk = pubkey2pk(Mypubkey());
e04b5c08 412 rewardspk = GetUnspendable(cp,0);
d549130d 413 sbits = stringbits(planstr);
db6bfcc4 414 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
eac2c15e 415 {
2205fd65 416 fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
eac2c15e 417 return(0);
418 }
88acd162 419 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
194ad5b8 420 {
cfea7a46 421 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
88acd162 422 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
6e281d1c 423 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
194ad5b8 424 }
88acd162 425 fprintf(stderr,"cant find enough inputs\n");
194ad5b8 426 return(0);
427}
428
46bbc433 429std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
e95b9582 430{
db6bfcc4 431 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
46bbc433 432 if ( amount < 0 )
433 {
434 fprintf(stderr,"negative parameter error\n");
435 return(0);
436 }
e95b9582 437 cp = CCinit(&C,EVAL_REWARDS);
438 if ( txfee == 0 )
439 txfee = 10000;
440 mypk = pubkey2pk(Mypubkey());
441 rewardspk = GetUnspendable(cp,0);
d549130d 442 sbits = stringbits(planstr);
db6bfcc4 443 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
e95b9582 444 {
eac2c15e 445 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
446 return(0);
e95b9582 447 }
d4e61763 448 sbits = stringbits(planstr);
eac2c15e 449 if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
450 {
451 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
6e281d1c 452 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
eac2c15e 453 } else fprintf(stderr,"cant find enough inputs\n");
454 fprintf(stderr,"cant find fundingtxid\n");
e95b9582 455 return(0);
456}
457
46bbc433 458std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
194ad5b8 459{
87ccafbd 460 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
46bbc433 461 if ( deposit < 0 )
462 {
463 fprintf(stderr,"negative parameter error\n");
464 return(0);
465 }
e04b5c08 466 cp = CCinit(&C,EVAL_REWARDS);
85c54375 467 if ( txfee == 0 )
468 txfee = 10000;
194ad5b8 469 mypk = pubkey2pk(Mypubkey());
cfea7a46 470 rewardspk = GetUnspendable(cp,0);
fac87764 471 sbits = stringbits(planstr);
1d801ae6 472 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
473 {
474 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
475 return(0);
476 }
477 if ( deposit < mindeposit )
478 {
479 fprintf(stderr,"Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
480 return(0);
481 }
482 if ( (funding= RewardsPlanFunds(sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
194ad5b8 483 {
791cf78c 484 if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
cfea7a46 485 {
1d801ae6 486 mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
791cf78c 487 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
6e281d1c 488 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
9d2841c3 489 } else fprintf(stderr,"cant find enough inputs %.8f note enough for %.8f\n",(double)funding/COIN,(double)deposit/COIN);
cfea7a46 490 }
88acd162 491 fprintf(stderr,"cant find rewards inputs\n");
194ad5b8 492 return(0);
493}
494
9d2841c3 495std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
496{
d5dae053 497 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;
9d2841c3 498 cp = CCinit(&C,EVAL_REWARDS);
499 if ( txfee == 0 )
500 txfee = 10000;
501 rewardspk = GetUnspendable(cp,0);
502 mypk = pubkey2pk(Mypubkey());
503 sbits = stringbits(planstr);
504 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
505 {
506 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
507 return(0);
508 }
791cf78c 509 // need to deal with finding the right utxos
9d2841c3 510 if ( locktxid == zeroid )
4f839b5c 511 amount = AddRewardsInputs(scriptPubKey,0,cp,mtx,rewardspk,(1LL << 30),1);
9d2841c3 512 else
513 {
1d0c7762 514 GetCCaddress(cp,coinaddr,rewardspk);
9d2841c3 515 if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
516 {
b938be96 517 fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
9d2841c3 518 return(0);
519 }
d5dae053 520 if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
521 {
4f839b5c 522 scriptPubKey = tx.vout[1].scriptPubKey;
d5dae053 523 mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
524 }
525 else
526 {
527 fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
528 return(0);
529 }
9d2841c3 530 }
d5dae053 531 if ( amount > 0 && (reward= RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit)) > txfee && scriptPubKey.size() > 0 )
9d2841c3 532 {
6e801a82 533 if ( (inputs= AddRewardsInputs(ignore,1,cp,mtx,rewardspk,reward+txfee,30)) > 0 )
9d2841c3 534 {
d5dae053 535 if ( inputs >= (reward + 2*txfee) )
536 CCchange = (inputs - (reward + txfee));
82381395 537 fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN);
31ffb5c5 538 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
d5dae053 539 mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey));
d2aba1d6 540 return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
9d2841c3 541 }
542 fprintf(stderr,"cant find enough rewards inputs\n");
543 }
544 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);
545 return(0);
546}
194ad5b8 547
This page took 0.119289 seconds and 4 git commands to generate.