]> Git Repo - VerusCoin.git/blob - src/cc/channels.cpp
Improve support for ID address differentiation and begin referral validation
[VerusCoin.git] / src / cc / channels.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 "CCchannels.h"
17
18 /*
19  The idea here is to allow instant (mempool) payments that are secured by dPoW. In order to simplify things, channels CC will require creating reserves for each payee locked in the destination user's CC address. This will look like the payment is already made, but it is locked until further released. The dPoW protection comes from the cancel channel having a delayed effect until the next notarization. This way, if a payment release is made and the chain reorged, the same payment release will still be valid when it is re-broadcast into the mempool.
20  
21  In order to achieve this effect, the payment release needs to be in a special form where its input cannot be spent only by the sender.
22
23  Given sender's payment to dest CC address, only the destination is able to spend, so we need to constrain that spending with a release mechanism. One idea is a 2of2 multisig, but that has the issue of needing confirmation and since a sender utxo is involved, subject to doublespend and we lose the speed. Another idea is release on secrets! since once revealed, the secret remains valid, this method is immune from double spend. Also, there is no worry about an MITM attack as the funds are only spendable by the destination pubkey and only with the secret. The secrets can be sent via any means, including OP_RETURN of normal transaction in the mempool.
24  
25  Now the only remaining issue for sending is how to allocate funds to the secrets. This needs to be sent as hashes of secrets when the channel is created. A bruteforce method would be one secret per COIN, but for large amount channels this is cumbersome. A more practical approach is to have a set of secrets for each order of magnitude:
26  
27  123.45 channel funds -> 1x secret100, 2x secret10, 3x secret1, 4x secret.1, 5x secret.01
28  15 secrets achieves the 123.45 channel funding.
29  
30  In order to avoid networking issues, the convention can be to send tx to normal address of destination with just an OP_RETURN, for the cost of txfee. For micropayments, a separate method of secret release needs to be established, but that is beyond the scope of this CC.
31  
32  There is now the dPoW security that needs to be ensured. In order to close the channel, a tx needs to be confirmed that cancels the channel. As soon as this tx is seen, the destination will know that the channel will be closing soon, if the node is online. If not, the payments cant be credited anyway, so it seems no harm. Even after the channel is closed, it is possible for secrets to be releasing funds, but depending on when the notarization happens, it could invalidate the spends, so it is safest that as soon as the channel cancel tx is confirmed to invalidate any further payments released.
33  
34  Given a channelclose and notarization confirmation (or enough blocks), the remaining funds needs to be able to come back to the sender. this means the funds need to be in a 1of2 CC multisig to allow either party to spend. Cheating is prevented by the additional rules of spending, ie. denomination secrets, or channelclose.
35  
36  For efficiency we want to allow batch spend with multiple secrets to claim a single total
37  
38  Second iteration:
39  As implementing it, some efficieny gains to be made with a slightly different approach.
40  Instead of separate secrets for each amount, a hashchain will be used, each releasing the same amount
41  
42  To spend, the prior value in the hash chain is published, or can publish N deep. validation takes N hashes.
43  
44  Also, in order to be able to track open channels, a tag is needed to be sent and better to send to a normal CC address for a pubkey to isolate the transactions for channel opens.
45  
46 Possible third iteration:
47  Let us try to setup a single "hot wallet" address to have all the channel funds and use it for payments to any destination. If there are no problems with reorgs and double spends, this would allow everyone to be "connected" to everyone else via the single special address.
48  
49  So funds -> user's CC address along with hashchain, but likely best to have several utxo to span order of magnitudes.
50  
51  a micropayment would then spend a utxo and attach a shared secret encoded unhashed link from the hashchain. That makes the receiver the only one that can decode the actual hashchain's prior value.
52  
53  however, since this spend is only spendable by the sender, it is subject to a double spend attack. It seems it is a dead end. Alternative is to use the global CC address, but that commingles all funds from all users and any accounting error puts all funds at risk.
54  
55  So, back to the second iteration, which is the only one so far that is immune from doublespend attack as the funds are already in the destination's CC address. One complication is that due to CC sorting of pubkeys, the address for sending and receiving is the same, so the destination pubkey needs to be attached to each opreturn.
56  
57  Now when the prior hashchain value is sent via payment, it allows the receiver to spend the utxo, so the only protection needed is to prevent channel close from invalidating already made payments.
58  
59  In order to allow multiple payments included in a single transaction, presentation of the N prior hashchain value can be used to get N payments and all the spends create a spending chain in sequential order of the hashchain.
60  
61 */
62
63 // start of consensus code
64
65 int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
66 {
67     char destaddr[64];
68     if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
69     {
70         if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
71             return(tx.vout[v].nValue);
72     }
73     return(0);
74 }
75
76 bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
77 {
78     static uint256 zerohash;
79     CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
80     numvins = tx.vin.size();
81     numvouts = tx.vout.size();
82     for (i=0; i<numvins; i++)
83     {
84         //fprintf(stderr,"vini.%d\n",i);
85         if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
86         {
87             //fprintf(stderr,"vini.%d check mempool\n",i);
88             if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
89                 return eval->Invalid("cant find vinTx");
90             else
91             {
92                 //fprintf(stderr,"vini.%d check hash and vout\n",i);
93                 if ( hashBlock == zerohash )
94                     return eval->Invalid("cant Channels from mempool");
95                 if ( (assetoshis= IsChannelsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
96                     inputs += assetoshis;
97             }
98         }
99     }
100     for (i=0; i<numvouts; i++)
101     {
102         //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
103         if ( (assetoshis= IsChannelsvout(cp,tx,i)) != 0 )
104             outputs += assetoshis;
105     }
106     if ( inputs != outputs+txfee )
107     {
108         fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
109         return eval->Invalid("mismatched inputs != outputs + txfee");
110     }
111     else return(true);
112 }
113
114 bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
115 {
116     int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
117     return(false);
118     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
119     numvins = tx.vin.size();
120     numvouts = tx.vout.size();
121     preventCCvins = preventCCvouts = -1;
122     if ( numvouts < 1 )
123         return eval->Invalid("no vouts");
124     else
125     {
126         for (i=0; i<numvins; i++)
127         {
128             if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
129             {
130                 return eval->Invalid("illegal normal vini");
131             }
132         }
133         //fprintf(stderr,"check amounts\n");
134         if ( ChannelsExactAmounts(cp,eval,tx,1,10000) == false )
135         {
136             fprintf(stderr,"Channelsget invalid amount\n");
137             return false;
138         }
139         else
140         {
141             txid = tx.GetHash();
142             memcpy(hash,&txid,sizeof(hash));
143             retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
144             if ( retval != 0 )
145                 fprintf(stderr,"Channelsget validated\n");
146             else fprintf(stderr,"Channelsget invalid\n");
147             return(retval);
148         }
149     }
150 }
151 // end of consensus code
152
153 // helper functions for rpc calls in rpcwallet.cpp
154
155 CScript EncodeChannelsOpRet(uint8_t funcid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain)
156 {
157     CScript opret; uint8_t evalcode = EVAL_CHANNELS;
158     opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << srcpub << destpub << numpayments << payment << hashchain);
159     return(opret);
160 }
161
162 uint8_t DecodeChannelsOpRet(uint256 txid,const CScript &scriptPubKey,CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain)
163 {
164     std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid;
165     GetOpReturnData(scriptPubKey, vopret);
166     if ( vopret.size() > 2 )
167     {
168         script = (uint8_t *)vopret.data();
169         if ( script[0] == EVAL_CHANNELS )
170         {
171             if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 )
172             {
173                 return(f);
174             }
175         } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]);
176     } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
177     return(0);
178 }
179
180 int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
181 {
182     char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
183     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
184     GetCCaddress(cp,coinaddr,pk);
185     SetCCunspents(unspentOutputs,coinaddr);
186     for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
187     {
188         txid = it->first.txhash;
189         vout = (int32_t)it->first.index;
190         // no need to prevent dup
191         if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
192         {
193             if ( (nValue= IsChannelsvout(cp,vintx,vout)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
194             {
195                 if ( total != 0 && maxinputs != 0 )
196                     mtx.vin.push_back(CTxIn(txid,vout,CScript()));
197                 nValue = it->second.satoshis;
198                 totalinputs += nValue;
199                 n++;
200                 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
201                     break;
202             }
203         }
204     }
205     return(totalinputs);
206 }
207
208 std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment)
209 {
210     CMutableTransaction mtx; uint8_t hash[32],hashdest[32]; uint64_t funds; int32_t i; uint256 hashchain,entropy,hentropy; CPubKey mypk; struct CCcontract_info *cp,C;
211     if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS )
212     {
213         CCerror = strprintf("invalid ChannelsFund param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment);
214         fprintf(stderr,"%s\n",CCerror.c_str());
215         return("");
216     }
217     cp = CCinit(&C,EVAL_CHANNELS);
218     if ( txfee == 0 )
219         txfee = 10000;
220     mypk = pubkey2pk(Mypubkey());
221     funds = numpayments * payment;
222     if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
223     {
224         hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash);
225         endiancpy(hash,(uint8_t *)&hentropy,32);
226         for (i=0; i<numpayments; i++)
227         {
228             vcalc_sha256(0,hashdest,hash,32);
229             memcpy(hash,hashdest,32);
230         }
231         endiancpy((uint8_t *)&hashchain,hashdest,32);
232         mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub));
233         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
234         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub));
235         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',mypk,destpub,numpayments,payment,hashchain)));
236     }
237     return("");
238 }
239
240 std::string ChannelStop(uint64_t txfee,CPubKey destpub,uint256 origtxid)
241 {
242     CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C;
243     // verify this is one of our outbound channels
244     cp = CCinit(&C,EVAL_CHANNELS);
245     if ( txfee == 0 )
246         txfee = 10000;
247     mypk = pubkey2pk(Mypubkey());
248     if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
249     {
250         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
251         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('S',mypk,destpub,0,0,zeroid)));
252     }
253     return("");
254 }
255
256 std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount)
257 {
258     CMutableTransaction mtx; CPubKey mypk,destpub; uint256 secret; struct CCcontract_info *cp,C; int32_t prevdepth;
259     // verify lasttxid and origtxid match and src is me
260     // also verify hashchain depth and amount, set prevdepth
261     cp = CCinit(&C,EVAL_CHANNELS);
262     if ( txfee == 0 )
263         txfee = 10000;
264     mypk = pubkey2pk(Mypubkey());
265     if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
266     {
267         // add locked funds inputs
268         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
269         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('P',mypk,destpub,prevdepth-n,amount,secret)));
270     }
271     return("");
272 }
273
274 std::string ChannelCollect(uint64_t txfee,uint256 paytxid,uint256 origtxid,int32_t n,int64_t amount)
275 {
276     CMutableTransaction mtx; CPubKey mypk,senderpub; struct CCcontract_info *cp,C; int32_t prevdepth;
277     // verify paytxid and origtxid match and dest is me
278     // also verify hashchain depth and amount
279     cp = CCinit(&C,EVAL_CHANNELS);
280     if ( txfee == 0 )
281         txfee = 10000;
282     mypk = pubkey2pk(Mypubkey());
283     if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
284     {
285         // add locked funds inputs
286         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
287         mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
288         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',senderpub,mypk,prevdepth-n,amount,paytxid)));
289     }
290     return("");
291 }
292
293 std::string ChannelRefund(uint64_t txfee,uint256 stoptxid,uint256 origtxid)
294 {
295     CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C; int64_t amount;
296     // verify stoptxid and origtxid match and are mine
297     cp = CCinit(&C,EVAL_CHANNELS);
298     if ( txfee == 0 )
299         txfee = 10000;
300     mypk = pubkey2pk(Mypubkey());
301     if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
302     {
303         mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
304         mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
305         return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',mypk,mypk,0,0,stoptxid)));
306     }
307     return("");
308 }
309
310 UniValue ChannelsInfo()
311 {
312     UniValue result(UniValue::VOBJ); CTransaction tx; uint256 txid,hashBlock,hashchain; struct CCcontract_info *cp,C; uint8_t funcid; char myCCaddr[64]; int32_t vout,numvouts,numpayments; int64_t nValue,payment; CPubKey srcpub,destpub,mypk;
313     std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
314     result.push_back(Pair("result","success"));
315     result.push_back(Pair("name","Channels"));
316     cp = CCinit(&C,EVAL_CHANNELS);
317     mypk = pubkey2pk(Mypubkey());
318     GetCCaddress(cp,myCCaddr,mypk);
319     SetCCtxids(txids,myCCaddr);
320     for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
321     {
322         //int height = it->first.blockHeight;
323         txid = it->first.txhash;
324         vout = (int32_t)it->first.index;
325         nValue = (int64_t)it->second;
326         if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
327         {
328             if ( DecodeChannelsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,srcpub,destpub,numpayments,payment,hashchain) == 'O' || funcid == 'P' )
329             {
330                 char str[67],str2[67];
331                 fprintf(stderr,"%s func.%c %s -> %s %.8f num.%d of %.8f\n",mypk == srcpub ? "send" : "recv",funcid,pubkey33_str(str,(uint8_t *)&srcpub),pubkey33_str(str2,(uint8_t *)&destpub),(double)tx.vout[0].nValue/COIN,numpayments,(double)payment/COIN);
332             }
333         }
334     }
335     return(result);
336 }
337
This page took 0.075836 seconds and 4 git commands to generate.