1 /******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
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. *
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 *
12 * Removal or modification of this copyright notice is prohibited. *
14 ******************************************************************************/
17 #include "../txmempool.h"
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.
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.
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.
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.
29 // start of consensus code
31 int64_t IsFaucetvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
34 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
36 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
37 return(tx.vout[v].nValue);
42 bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
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++)
50 //fprintf(stderr,"vini.%d\n",i);
51 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
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");
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 )
66 for (i=0; i<numvouts; i++)
68 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
69 if ( (assetoshis= IsFaucetvout(cp,tx,i)) != 0 )
70 outputs += assetoshis;
72 if ( inputs != outputs+FAUCETSIZE+txfee )
74 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
75 return eval->Invalid("mismatched inputs != outputs + FAUCETSIZE + txfee");
80 bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
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;
88 return eval->Invalid("no vouts");
91 for (i=0; i<numvins; i++)
93 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
95 fprintf(stderr,"faucetget invalid vini\n");
96 return eval->Invalid("illegal normal vini");
99 //fprintf(stderr,"check amounts\n");
100 if ( FaucetExactAmounts(cp,eval,tx,1,10000) == false )
102 fprintf(stderr,"faucetget invalid amount\n");
108 if ( IsFaucetvout(cp,tx,0) != 0 )
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++)
124 //int height = it->first.blockHeight;
125 if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 )
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");
131 retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
133 fprintf(stderr,"faucetget validated\n");
134 else fprintf(stderr,"faucetget invalid\n");
139 // end of consensus code
141 // helper functions for rpc calls in rpcwallet.cpp
143 int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
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++)
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 )
157 if ( (nValue= IsFaucetvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 )
159 if ( total != 0 && maxinputs != 0 )
160 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
161 nValue = it->second.satoshis;
162 totalinputs += nValue;
164 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
166 } else fprintf(stderr,"nValue too small or already spent in mempool\n");
167 } else fprintf(stderr,"couldnt get tx\n");
172 std::string FaucetGet(uint64_t txfee)
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);
178 faucetpk = GetUnspendable(cp,0);
179 mypk = pubkey2pk(Mypubkey());
180 if ( (inputs= AddFaucetInputs(cp,mtx,faucetpk,nValue+txfee,60)) > 0 )
182 if ( inputs > nValue )
183 CCchange = (inputs - nValue - txfee);
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++)
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 )
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 )
200 fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL));
203 //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
206 fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL));
208 } else fprintf(stderr,"cant find faucet inputs\n");
212 std::string FaucetFund(uint64_t txfee,int64_t funds)
214 CMutableTransaction mtx; CPubKey mypk,faucetpk; CScript opret; struct CCcontract_info *cp,C;
215 cp = CCinit(&C,EVAL_FAUCET);
218 mypk = pubkey2pk(Mypubkey());
219 faucetpk = GetUnspendable(cp,0);
220 if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 )
222 mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,funds,faucetpk));
223 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
228 UniValue FaucetInfo()
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));