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