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