]> Git Repo - VerusCoin.git/blame - src/cc/rewards.cpp
Add support for new identity "i" address as CtxDestination, which can specify global...
[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
984a9dc3 16#include "CCrewards.h"
194ad5b8 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
278a61fd 69int64_t RewardsCalc(int64_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;
51597670 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;
6ca2e998 80 if ( 0 ) // amount * APR * duration / COIN * 100 * 365*24*3600
81 reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100);
82 else reward = (((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000)) / 10000;
fefaa934 83 if ( reward > amount )
84 reward = amount;
6ca2e998 85 fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f vals %.8f %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN,(double)((amount * duration) / (365 * 24 * 3600LL))/COIN,(double)(((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000))/COIN);
194ad5b8 86 return(reward);
87}
88
88acd162 89CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
6dcd15b5 90{
91 CScript opret; uint8_t evalcode = EVAL_REWARDS;
88acd162 92 opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
93 return(opret);
94}
95
96uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
97{
98 std::vector<uint8_t> vopret; uint8_t *script,e,f;
99 GetOpReturnData(scriptPubKey, vopret);
100 script = (uint8_t *)vopret.data();
0af767e0 101 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
6dcd15b5 102 {
88acd162 103 if ( e == EVAL_REWARDS && f == 'F' )
104 return(f);
6dcd15b5 105 }
88acd162 106 return(0);
107}
108
c4e7f616 109CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
88acd162 110{
111 CScript opret; uint8_t evalcode = EVAL_REWARDS;
c4e7f616 112 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
6dcd15b5 113 return(opret);
114}
115
0af767e0 116uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
6dcd15b5 117{
9f2d38b3 118 std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; uint64_t APR,minseconds,maxseconds,mindeposit;
6dcd15b5 119 GetOpReturnData(scriptPubKey, vopret);
0af767e0 120 if ( vopret.size() > 2 )
6dcd15b5 121 {
0af767e0 122 script = (uint8_t *)vopret.data();
123 if ( script[0] == EVAL_REWARDS )
124 {
125 if ( script[1] == 'F' )
126 {
cb35ec92 127 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
85afb4f0 128 {
0af767e0 129 fundingtxid = txid;
85afb4f0 130 return('F');
131 } else fprintf(stderr,"unmarshal error for F\n");
0af767e0 132 }
133 else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
134 {
135 if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
136 return(f);
cb35ec92 137 else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
0af767e0 138 }
cb35ec92 139 } else fprintf(stderr,"script[0] %02x != EVAL_REWARDS\n",script[0]);
5641892d 140 } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
88acd162 141 return(0);
6dcd15b5 142}
143
278a61fd 144int64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
194ad5b8 145{
112803d8 146 char destaddr[64]; uint64_t sbits; uint256 fundingtxid,txid; uint8_t funcid; int32_t numvouts;
147 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= (int32_t)tx.vout.size()) > 0 )
194ad5b8 148 {
112803d8 149 txid = tx.GetHash();
150 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
151 {
152
153 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
154 return(tx.vout[v].nValue);
155 }
194ad5b8 156 }
157 return(0);
158}
159
112803d8 160bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
194ad5b8 161{
162 static uint256 zerohash;
278a61fd 163 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
194ad5b8 164 numvins = tx.vin.size();
165 numvouts = tx.vout.size();
166 for (i=0; i<numvins; i++)
167 {
287efad4 168 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
194ad5b8 169 {
170 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
171 return eval->Invalid("always should find vin, but didnt");
172 else
173 {
174 if ( hashBlock == zerohash )
175 return eval->Invalid("cant rewards from mempool");
112803d8 176 if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
194ad5b8 177 inputs += assetoshis;
178 }
179 }
180 }
181 for (i=0; i<numvouts; i++)
182 {
183 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
112803d8 184 if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
194ad5b8 185 outputs += assetoshis;
186 }
0af767e0 187 if ( inputs != outputs+txfee )
194ad5b8 188 {
674e0bbe 189 fprintf(stderr,"inputs %llu vs outputs %llu txfee %llu\n",(long long)inputs,(long long)outputs,(long long)txfee);
0af767e0 190 return eval->Invalid("mismatched inputs != outputs + txfee");
194ad5b8 191 }
192 else return(true);
193}
194
8a727a26 195bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
194ad5b8 196{
717ede07 197 uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,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 198 numvins = tx.vin.size();
199 numvouts = tx.vout.size();
200 preventCCvins = preventCCvouts = -1;
201 if ( numvouts < 1 )
202 return eval->Invalid("no vouts");
203 else
204 {
0af767e0 205 txid = tx.GetHash();
206 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
194ad5b8 207 {
0af767e0 208 if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
209 return eval->Invalid("cant find fundingtxid");
210 else if ( fundingTx.vout.size() > 0 && DecodeRewardsFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 'F' )
211 return eval->Invalid("fundingTx not valid");
59da6d30 212 if ( APR > REWARDSCC_MAXAPR )
213 return eval->Invalid("excessive APR");
0af767e0 214 switch ( funcid )
194ad5b8 215 {
0af767e0 216 case 'F':
217 //vins.*: normal inputs
218 //vout.0: CC vout for funding
219 //vout.1: normal marker vout for easy searching
220 //vout.2: normal change
221 //vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
222 return eval->Invalid("unexpected RewardsValidate for createfunding");
223 break;
224 case 'A':
225 //vins.*: normal inputs
226 //vout.0: CC vout for funding
227 //vout.1: normal change
228 //vout.n-1: opreturn 'A' sbits fundingtxid
229 return eval->Invalid("unexpected RewardsValidate for addfunding");
230 break;
231 case 'L':
232 //vins.*: normal inputs
233 //vout.0: CC vout for locked funds
234 //vout.1: normal output to unlock address
235 //vout.2: change
236 //vout.n-1: opreturn 'L' sbits fundingtxid
237 return eval->Invalid("unexpected RewardsValidate for lock");
238 break;
239 case 'U':
240 //vin.0: locked funds CC vout.0 from lock
241 //vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
bc329969 242 //vout.0: funding CC change or recover normal payout
0af767e0 243 //vout.1: normal output to unlock address
244 //vout.n-1: opreturn 'U' sbits fundingtxid
8254d65a 245 if ( fundingtxid == tx.vin[0].prevout.hash )
6265d225 246 return eval->Invalid("cant unlock fundingtxid");
247 else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
bc329969 248 return eval->Invalid("always should find vin.0, but didnt");
717ede07 249 else if ( DecodeRewardsOpRet(tx.vin[0].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid) != 'L' )
250 return eval->Invalid("can only unlock locktxid");
251 else if ( fundingtxid != vinfundingtxid )
252 return eval->Invalid("mismatched vinfundingtxid");
0af767e0 253 for (i=0; i<numvins; i++)
254 {
255 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
256 return eval->Invalid("unexpected normal vin for unlock");
257 }
799f664f 258 if ( numvouts == 2 && numvins == 1 )
bc329969 259 {
260 if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 )
261 return eval->Invalid("unlock recover tx vout.0 is not normal output");
262 else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey )
263 return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey");
264 else if ( tx.vout[0].nValue > vinTx.vout[0].nValue )
265 return eval->Invalid("unlock recover tx vout.0 mismatched amounts");
799f664f 266 else if ( tx.vout[1].nValue > 0 )
267 return eval->Invalid("unlock recover tx vout.1 nonz amount");
bc329969 268 else return(true);
269 }
270 if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
8b10fb48 271 return eval->Invalid("unlock tx vout.0 is normal output");
272 else if ( numvouts != 3 )
273 return eval->Invalid("unlock tx wrong number of vouts");
0af767e0 274 else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
275 return eval->Invalid("unlock tx vout.0 is normal output");
9f2d38b3 276 else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
0af767e0 277 return eval->Invalid("unlock tx vout.1 is CC output");
763ec3ca 278 else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
279 return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
0af767e0 280 amount = vinTx.vout[0].nValue;
43b91cca 281 reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
21565974 282 if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 )
4f054db0 283 return false;
284 else if ( tx.vout[1].nValue > amount+reward )
0af767e0 285 return eval->Invalid("unlock tx vout.1 isnt amount+reward");
8b10fb48 286 else if ( tx.vout[2].nValue > 0 )
287 return eval->Invalid("unlock tx vout.2 isnt 0");
0af767e0 288 preventCCvouts = 1;
289 break;
290 }
194ad5b8 291 }
0af767e0 292 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
194ad5b8 293 }
294 return(true);
295}
296
50dfc224 297static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t refsbits,uint256 reffundingtxid,uint64_t needed)
6ca2e998 298{
299 uint8_t funcid; uint64_t sbits,nValue; uint256 fundingtxid; char str[65];
300 memset(&txid,0,sizeof(txid));
301 vout = -1;
302 nValue = 0;
303 BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
304 {
305 const CTransaction &tx = e.GetTx();
306 if ( tx.vout.size() > 0 && tx.vout[0].nValue >= needed )
307 {
308 const uint256 &hash = tx.GetHash();
309 if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(hash,0) == 0 )
310 {
311 if ( (funcid= DecodeRewardsOpRet(hash,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'U' && sbits == refsbits && fundingtxid == reffundingtxid )
312 {
313 txid = hash;
314 vout = 0;
315 nValue = tx.vout[0].nValue;
316 fprintf(stderr,"found 'U' %s %.8f in unspent in mempool\n",uint256_str(str,txid),(double)nValue/COIN);
317 return(nValue);
318 }
319 }
320 }
321 }
322 return(nValue);
323}
324
791cf78c 325// 'L' vs 'F' and 'A'
6ca2e998 326int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
194ad5b8 327{
6ca2e998 328 char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid;
194ad5b8 329 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
e04b5c08 330 GetCCaddress(cp,coinaddr,pk);
194ad5b8 331 SetCCunspents(unspentOutputs,coinaddr);
332 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
333 {
334 txid = it->first.txhash;
fac87764 335 vout = (int32_t)it->first.index;
93ac96fa 336 if ( it->second.satoshis < 1000000 )
c2342f7b 337 continue;
e65c27b5 338 //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
fac87764 339 for (j=0; j<mtx.vin.size(); j++)
340 if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
341 break;
342 if ( j != mtx.vin.size() )
343 continue;
6deb8c09 344 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
194ad5b8 345 {
0af767e0 346 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
194ad5b8 347 {
112803d8 348 if ( sbits != refsbits || fundingtxid != reffundingtxid )
349 continue;
6ca2e998 350 if ( maxseconds == 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
791cf78c 351 continue;
6ca2e998 352 else if ( maxseconds != 0 && funcid != 'L' )
353 {
6986a67b 354 if ( CCduration(numblocks,txid) < maxseconds )
6ca2e998 355 continue;
356 }
e65c27b5 357 fprintf(stderr,"maxseconds.%d (%c) %.8f %.8f\n",(int32_t)maxseconds,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
194ad5b8 358 if ( total != 0 && maxinputs != 0 )
d5dae053 359 {
6ca2e998 360 if ( maxseconds != 0 )
d5dae053 361 scriptPubKey = tx.vout[1].scriptPubKey;
fac87764 362 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
d5dae053 363 }
cfea7a46 364 totalinputs += it->second.satoshis;
194ad5b8 365 n++;
366 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
367 break;
93637d45 368 } else fprintf(stderr,"null funcid\n");
194ad5b8 369 }
370 }
6ca2e998 371 if ( maxseconds == 0 && totalinputs < total && (maxinputs == 0 || n < maxinputs-1) )
372 {
373 fprintf(stderr,"search mempool for unlocked and unspent CC rewards output for %.8f\n",(double)(total-totalinputs)/COIN);
374 if ( (nValue= myIs_unlockedtx_inmempool(txid,vout,refsbits,reffundingtxid,total-totalinputs)) > 0 )
375 {
376 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
377 fprintf(stderr,"added mempool vout for %.8f\n",(double)nValue/COIN);
378 totalinputs += nValue;
379 n++;
380 }
381 }
194ad5b8 382 return(totalinputs);
383}
384
6ca2e998 385int64_t RewardsPlanFunds(uint64_t &lockedfunds,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
cfea7a46 386{
278a61fd 387 char coinaddr[64]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
cfea7a46 388 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
6ca2e998 389 lockedfunds = 0;
cfea7a46 390 GetCCaddress(cp,coinaddr,pk);
391 SetCCunspents(unspentOutputs,coinaddr);
392 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
393 {
fac87764 394 txid = it->first.txhash;
395 vout = (int32_t)it->first.index;
396 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
cfea7a46 397 {
6ca2e998 398 if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'A' || funcid == 'U' || funcid == 'L' )
cfea7a46 399 {
a85ad9d3 400 if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
fac87764 401 {
a85ad9d3 402 if ( (nValue= IsRewardsvout(cp,tx,vout,sbits,fundingtxid)) > 0 )
6ca2e998 403 {
404 if ( funcid == 'L' )
405 lockedfunds += nValue;
406 else totalinputs += nValue;
407 }
2205fd65 408 else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
ba30129f 409 } //else fprintf(stderr,"else case\n");
64d610d4 410 } else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
cfea7a46 411 }
412 }
413 return(totalinputs);
414}
415
db6bfcc4 416bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
eac2c15e 417{
db6bfcc4 418 char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
fac87764 419 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
4610bb91 420 GetCCaddress(cp,CCaddr,rewardspk);
421 SetCCtxids(txids,CCaddr);
fac87764 422 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
423 {
424 //int height = it->first.blockHeight;
425 txid = it->first.txhash;
d18420d2 426 if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
fac87764 427 {
d0d96ab0 428 //char str[65]; fprintf(stderr,"rewards plan %s\n",uint256_str(str,txid));
d18420d2 429 if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
fac87764 430 {
431 if ( sbits == refsbits )
432 return(true);
433 }
434 }
435 }
436 return(false);
eac2c15e 437}
438
fdd22810 439UniValue RewardsInfo(uint256 rewardsid)
440{
6ca2e998 441 UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; uint64_t lockedfunds,APR,minseconds,maxseconds,mindeposit,sbits,funding; CPubKey rewardspk; struct CCcontract_info *cp,C; char str[67],numstr[65];
fdd22810 442 if ( GetTransaction(rewardsid,vintx,hashBlock,false) == 0 )
443 {
bbf9b82f 444 fprintf(stderr,"cant find fundingtxid\n");
efa644f2 445 result.push_back(Pair("result","error"));
bbf9b82f 446 result.push_back(Pair("error","cant find fundingtxid"));
447 return(result);
fdd22810 448 }
bbf9b82f 449 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
fdd22810 450 {
bbf9b82f 451 fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
efa644f2 452 result.push_back(Pair("result","error"));
bbf9b82f 453 result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
454 return(result);
fdd22810 455 }
456 result.push_back(Pair("result","success"));
457 result.push_back(Pair("fundingtxid",uint256_str(str,rewardsid)));
34589c80 458 unstringbits(str,sbits);
459 result.push_back(Pair("name",str));
d4e61763 460 result.push_back(Pair("sbits",sbits));
fdd22810 461 sprintf(numstr,"%.8f",(double)APR/COIN);
462 result.push_back(Pair("APR",numstr));
463 result.push_back(Pair("minseconds",minseconds));
464 result.push_back(Pair("maxseconds",maxseconds));
465 sprintf(numstr,"%.8f",(double)mindeposit/COIN);
466 result.push_back(Pair("mindeposit",numstr));
94ada5ec 467 cp = CCinit(&C,EVAL_REWARDS);
468 rewardspk = GetUnspendable(cp,0);
6ca2e998 469 funding = RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,rewardsid);
94ada5ec 470 sprintf(numstr,"%.8f",(double)funding/COIN);
fdd22810 471 result.push_back(Pair("funding",numstr));
6ca2e998 472 sprintf(numstr,"%.8f",(double)lockedfunds/COIN);
473 result.push_back(Pair("locked",numstr));
fdd22810 474 return(result);
475}
476
477UniValue RewardsList()
478{
9fa1bcd2 479 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 480 cp = CCinit(&C,EVAL_REWARDS);
481 SetCCtxids(addressIndex,cp->normaladdr);
482 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
483 {
484 txid = it->first.txhash;
485 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
486 {
9fa1bcd2 487 if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
fdd22810 488 {
489 result.push_back(uint256_str(str,txid));
490 }
491 }
492 }
493 return(result);
494}
495
8eee6bd8 496std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
194ad5b8 497{
db6bfcc4 498 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
d587c96c 499 if ( funds < COIN || mindeposit < 0 || minseconds < 0 || maxseconds < 0 )
46bbc433 500 {
501 fprintf(stderr,"negative parameter error\n");
1ed46fb8 502 return("");
46bbc433 503 }
59da6d30 504 if ( APR > REWARDSCC_MAXAPR )
505 {
984a9dc3 506 fprintf(stderr,"25%% APR is maximum\n");
1ed46fb8 507 return("");
59da6d30 508 }
e04b5c08 509 cp = CCinit(&C,EVAL_REWARDS);
194ad5b8 510 if ( txfee == 0 )
511 txfee = 10000;
512 mypk = pubkey2pk(Mypubkey());
e04b5c08 513 rewardspk = GetUnspendable(cp,0);
d549130d 514 sbits = stringbits(planstr);
db6bfcc4 515 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
eac2c15e 516 {
2205fd65 517 fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
1ed46fb8 518 return("");
eac2c15e 519 }
88acd162 520 if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
194ad5b8 521 {
cfea7a46 522 mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
88acd162 523 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
6e281d1c 524 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
194ad5b8 525 }
88acd162 526 fprintf(stderr,"cant find enough inputs\n");
1ed46fb8 527 return("");
194ad5b8 528}
529
46bbc433 530std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
e95b9582 531{
db6bfcc4 532 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
46bbc433 533 if ( amount < 0 )
534 {
535 fprintf(stderr,"negative parameter error\n");
1ed46fb8 536 return("");
46bbc433 537 }
e95b9582 538 cp = CCinit(&C,EVAL_REWARDS);
539 if ( txfee == 0 )
540 txfee = 10000;
541 mypk = pubkey2pk(Mypubkey());
542 rewardspk = GetUnspendable(cp,0);
d549130d 543 sbits = stringbits(planstr);
db6bfcc4 544 if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
e95b9582 545 {
a03146b3
JDL
546 CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
547 fprintf(stderr,"%s\n",CCerror.c_str());
1ed46fb8 548 return("");
e95b9582 549 }
d4e61763 550 sbits = stringbits(planstr);
eac2c15e 551 if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
552 {
553 mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
6e281d1c 554 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
a03146b3
JDL
555 } else {
556 CCerror = "cant find enough inputs";
557 fprintf(stderr,"%s\n", CCerror.c_str());
558 }
559 CCerror = "cant find fundingtxid";
560 fprintf(stderr,"%s\n", CCerror.c_str());
1ed46fb8 561 return("");
e95b9582 562}
563
46bbc433 564std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
194ad5b8 565{
6ca2e998 566 CMutableTransaction mtx; CPubKey mypk,rewardspk; CScript opret; uint64_t lockedfunds,sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
bc329969 567 if ( deposit < txfee )
46bbc433 568 {
a03146b3
JDL
569 CCerror = "deposit amount less than txfee";
570 fprintf(stderr,"%s\n",CCerror.c_str());
1ed46fb8 571 return("");
46bbc433 572 }
e04b5c08 573 cp = CCinit(&C,EVAL_REWARDS);
85c54375 574 if ( txfee == 0 )
575 txfee = 10000;
194ad5b8 576 mypk = pubkey2pk(Mypubkey());
cfea7a46 577 rewardspk = GetUnspendable(cp,0);
fac87764 578 sbits = stringbits(planstr);
1d801ae6 579 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
580 {
a03146b3
JDL
581 CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
582 fprintf(stderr,"%s\n",CCerror.c_str());
1ed46fb8 583 return("");
1d801ae6 584 }
585 if ( deposit < mindeposit )
586 {
a03146b3
JDL
587 CCerror = strprintf("Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
588 fprintf(stderr,"%s\n",CCerror.c_str());
1ed46fb8 589 return("");
1d801ae6 590 }
6ca2e998 591 if ( (funding= RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
194ad5b8 592 {
791cf78c 593 if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
cfea7a46 594 {
1d801ae6 595 mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
791cf78c 596 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
6e281d1c 597 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
a03146b3
JDL
598 } else {
599 CCerror = strprintf("cant find enough inputs %.8f not enough for %.8f, make sure you imported privkey for the -pubkey address\n",(double)funding/COIN,(double)deposit/COIN);
600 fprintf(stderr,"%s\n",CCerror.c_str());
601 }
cfea7a46 602 }
f344c693 603 fprintf(stderr,"cant find rewards inputs funding %.8f locked %.8f vs deposit %.8f\n",(double)funding/COIN,(double)lockedfunds/COIN,(double)deposit/COIN);
1ed46fb8 604 return("");
194ad5b8 605}
606
9d2841c3 607std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
608{
de70190c 609 CMutableTransaction mtx,firstmtx; CTransaction tx; char coinaddr[64]; CPubKey mypk,rewardspk; CScript scriptPubKey,ignore; uint256 hashBlock; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; int64_t funding,reward=0,amount=0,inputs,CCchange=0; struct CCcontract_info *cp,C;
9d2841c3 610 cp = CCinit(&C,EVAL_REWARDS);
611 if ( txfee == 0 )
612 txfee = 10000;
613 rewardspk = GetUnspendable(cp,0);
614 mypk = pubkey2pk(Mypubkey());
615 sbits = stringbits(planstr);
6265d225 616 if ( locktxid == fundingtxid )
617 {
618 fprintf(stderr,"Rewards plan cant unlock fundingtxid\n");
619 CCerror = "Rewards plan cant unlock fundingtxid";
620 return("");
621 }
9d2841c3 622 if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
623 {
624 fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
8e0ff2b7 625 CCerror = "Rewards plan does not exist";
1ed46fb8 626 return("");
9d2841c3 627 }
ba30129f 628 fprintf(stderr,"APR %.8f minseconds.%llu maxseconds.%llu mindeposit %.8f\n",(double)APR/COIN,(long long)minseconds,(long long)maxseconds,(double)mindeposit/COIN);
9d2841c3 629 if ( locktxid == zeroid )
6ca2e998 630 amount = AddRewardsInputs(scriptPubKey,maxseconds,cp,mtx,rewardspk,(1LL << 30),1,sbits,fundingtxid);
9d2841c3 631 else
632 {
1d0c7762 633 GetCCaddress(cp,coinaddr,rewardspk);
9d2841c3 634 if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 )
635 {
b938be96 636 fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
8e0ff2b7 637 CCerror = "locktxid/v0 is spent";
1ed46fb8 638 return("");
9d2841c3 639 }
d5dae053 640 if ( GetTransaction(locktxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
641 {
4f839b5c 642 scriptPubKey = tx.vout[1].scriptPubKey;
d5dae053 643 mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
644 }
645 else
646 {
647 fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
8e0ff2b7 648 CCerror = "no normal vout.1 in locktxid";
1ed46fb8 649 return("");
d5dae053 650 }
9d2841c3 651 }
bc329969 652 if ( amount > txfee )
653 {
654 reward = RewardsCalc(amount,mtx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit);
655 if ( scriptPubKey.size() > 0 )
656 {
657 if ( reward > txfee )
658 {
8b10fb48 659 firstmtx = mtx;
093487f6 660 if ( (inputs= AddRewardsInputs(ignore,0,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) >= reward+txfee )
bc329969 661 {
662 if ( inputs >= (reward + 2*txfee) )
663 CCchange = (inputs - (reward + txfee));
664 fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN);
665 mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
666 mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey));
667 return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
668 }
669 else
670 {
8b10fb48 671 firstmtx.vout.push_back(CTxOut(amount-txfee,scriptPubKey));
bc329969 672 //CCerror = "cant find enough rewards inputs";
276c6548 673 fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN);
de70190c 674 return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
bc329969 675 }
e4f4e63b 676 }
bc329969 677 else
678 {
8e0ff2b7
JDL
679 CCerror = strprintf("reward %.8f is <= the transaction fee", reward);
680 fprintf(stderr,"%s\n", CCerror.c_str());
bc329969 681 }
682 }
683 else
684 {
e4f4e63b
JDL
685 CCerror = "invalid scriptPubKey";
686 fprintf(stderr,"%s\n", CCerror.c_str());
bc329969 687 }
688 }
689 else
690 {
691 CCerror = "amount must be more than txfee";
692 fprintf(stderr,"%s\n", CCerror.c_str());
9d2841c3 693 }
694 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);
1ed46fb8 695 return("");
9d2841c3 696}
194ad5b8 697
This page took 0.27294 seconds and 4 git commands to generate.