]> Git Repo - VerusCoin.git/blob - src/cc/faucet.cpp
Update without PBaaS for interim release
[VerusCoin.git] / src / cc / faucet.cpp
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 "CCfaucet.h"
17 #include "../txmempool.h"
18
19 /*
20  This file implements a simple CC faucet as an example of how to make a new CC contract. It wont have any fancy sybil protection but will serve the purpose of a fully automated faucet.
21  
22  In order to implement a faucet, we need to have it funded. Once it is funded, anybody should be able to get some reasonable small amount.
23  
24  This leads to needing to lock the funding in a CC protected output. And to put a spending limit. We can do a per transaction spending limit quite easily with vout constraints. However, that would allow anybody to issue thousands of transactions per block, so we also need to add a rate limiter.
25  
26  To implement this, we can simply make any faucet vout fund the faucet. Then we can limit the number of confirmed utxo by combining faucet outputs and then only using utxo which are confirmed. This combined with a vout size limit will drastically limit the funds that can be withdrawn from the faucet.
27 */
28
29 // start of consensus code
30
31 int64_t IsFaucetvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
32 {
33     char destaddr[64];
34     if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
35     {
36         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
37             return(tx.vout[v].nValue);
38     }
39     return(0);
40 }
41
42 bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
43 {
44     static uint256 zerohash;
45     CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
46     numvins = tx.vin.size();
47     numvouts = tx.vout.size();
48     for (i=0; i<numvins; i++)
49     {
50         //fprintf(stderr,"vini.%d\n",i);
51         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
52         {
53             //fprintf(stderr,"vini.%d check mempool\n",i);
54             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
55                 return eval->Invalid("cant find vinTx");
56             else
57             {
58                 //fprintf(stderr,"vini.%d check hash and vout\n",i);
59                 if ( hashBlock == zerohash )
60                     return eval->Invalid("cant faucet from mempool");
61                 if ( (assetoshis= IsFaucetvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
62                     inputs += assetoshis;
63             }
64         }
65     }
66     for (i=0; i<numvouts; i++)
67     {
68         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
69         if ( (assetoshis= IsFaucetvout(cp,tx,i)) != 0 )
70             outputs += assetoshis;
71     }
72     if ( inputs != outputs+FAUCETSIZE+txfee )
73     {
74         fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
75         return eval->Invalid("mismatched inputs != outputs + FAUCETSIZE + txfee");
76     }
77     else return(true);
78 }
79
80 bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
81 {
82     int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
83     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
84     numvins = tx.vin.size();
85     numvouts = tx.vout.size();
86     preventCCvins = preventCCvouts = -1;
87     if ( numvouts < 1 )
88         return eval->Invalid("no vouts");
89     else
90     {
91         for (i=0; i<numvins; i++)
92         {
93             if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
94             {
95                 fprintf(stderr,"faucetget invalid vini\n");
96                 return eval->Invalid("illegal normal vini");
97             }
98         }
99         //fprintf(stderr,"check amounts\n");
100         if ( FaucetExactAmounts(cp,eval,tx,1,10000) == false )
101         {
102             fprintf(stderr,"faucetget invalid amount\n");
103             return false;
104         }
105         else
106         {
107             preventCCvouts = 1;
108             if ( IsFaucetvout(cp,tx,0) != 0 )
109             {
110                 preventCCvouts++;
111                 i = 1;
112             } else i = 0;
113             txid = tx.GetHash();
114             memcpy(hash,&txid,sizeof(hash));
115             fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]);
116             if ( tx.vout[i].nValue != FAUCETSIZE )
117                 return eval->Invalid("invalid faucet output");
118             else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 )
119                 return eval->Invalid("invalid faucetget txid");
120             Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
121             SetCCtxids(txids,destaddr);
122             for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
123             {
124                 //int height = it->first.blockHeight;
125                 if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 )
126                 {
127                     //fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks);
128                     return eval->Invalid("faucet is only for brand new addresses");
129                 }
130             }
131             retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
132             if ( retval != 0 )
133                 fprintf(stderr,"faucetget validated\n");
134             else fprintf(stderr,"faucetget invalid\n");
135             return(retval);
136         }
137     }
138 }
139 // end of consensus code
140
141 // helper functions for rpc calls in rpcwallet.cpp
142
143 int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
144 {
145     char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
146     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
147     GetCCaddress(cp,coinaddr,pk);
148     SetCCunspents(unspentOutputs,coinaddr);
149     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
150     {
151         txid = it->first.txhash;
152         vout = (int32_t)it->first.index;
153         //char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
154         // no need to prevent dup
155         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
156         {
157             if ( (nValue= IsFaucetvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 )
158             {
159                 if ( total != 0 && maxinputs != 0 )
160                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
161                 nValue = it->second.satoshis;
162                 totalinputs += nValue;
163                 n++;
164                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
165                     break;
166             } else fprintf(stderr,"nValue too small or already spent in mempool\n");
167         } else fprintf(stderr,"couldnt get tx\n");
168     }
169     return(totalinputs);
170 }
171
172 std::string FaucetGet(uint64_t txfee)
173 {
174     CMutableTransaction mtx,tmpmtx; CPubKey mypk,faucetpk; int64_t inputs,CCchange=0,nValue=FAUCETSIZE; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash;
175     cp = CCinit(&C,EVAL_FAUCET);
176     if ( txfee == 0 )
177         txfee = 10000;
178     faucetpk = GetUnspendable(cp,0);
179     mypk = pubkey2pk(Mypubkey());
180     if ( (inputs= AddFaucetInputs(cp,mtx,faucetpk,nValue+txfee,60)) > 0 )
181     {
182         if ( inputs > nValue )
183             CCchange = (inputs - nValue - txfee);
184         if ( CCchange != 0 )
185             mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,CCchange,faucetpk));
186         mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
187         fprintf(stderr,"start at %u\n",(uint32_t)time(NULL));
188         j = rand() & 0xfffffff;
189         for (i=0; i<1000000; i++,j++)
190         {
191             tmpmtx = mtx;
192             rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET << (uint8_t)'G' << j));
193             if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 )
194             {
195                 len >>= 1;
196                 decode_hex(buf,len,(char *)rawhex.c_str());
197                 hash = bits256_doublesha256(0,buf,len);
198                 if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 )
199                 {
200                     fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL));
201                     return(rawhex);
202                 }
203                 //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
204             }
205         }
206         fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL));
207         return("");
208     } else fprintf(stderr,"cant find faucet inputs\n");
209     return("");
210 }
211
212 std::string FaucetFund(uint64_t txfee,int64_t funds)
213 {
214     CMutableTransaction mtx; CPubKey mypk,faucetpk; CScript opret; struct CCcontract_info *cp,C;
215     cp = CCinit(&C,EVAL_FAUCET);
216     if ( txfee == 0 )
217         txfee = 10000;
218     mypk = pubkey2pk(Mypubkey());
219     faucetpk = GetUnspendable(cp,0);
220     if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 )
221     {
222         mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,funds,faucetpk));
223         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
224     }
225     return("");
226 }
227
228 UniValue FaucetInfo()
229 {
230     UniValue result(UniValue::VOBJ); char numstr[64];
231     CMutableTransaction mtx; CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding;
232     result.push_back(Pair("result","success"));
233     result.push_back(Pair("name","Faucet"));
234     cp = CCinit(&C,EVAL_FAUCET);
235     faucetpk = GetUnspendable(cp,0);
236     funding = AddFaucetInputs(cp,mtx,faucetpk,0,0);
237     sprintf(numstr,"%.8f",(double)funding/COIN);
238     result.push_back(Pair("funding",numstr));
239     return(result);
240 }
241
This page took 0.038533 seconds and 4 git commands to generate.