]> Git Repo - VerusCoin.git/blob - src/cc/assets.cpp
Fix
[VerusCoin.git] / src / cc / assets.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 "CCassets.h"
17
18 /*
19  Assets can be created or transferred.
20  
21  native coins are also locked in the EVAL_ASSETS address, so we need a strict rule on when utxo in the special address are native coins and when they are assets. The specific rule that must not be violated is that vout0 for 'b'/'B' funcid are native coins. All other utxo locked in the special address are assets.
22  
23  To create an asset use CC EVAL_ASSETS to create a transaction where vout[0] funds the assets. Externally each satoshi can be interpreted to represent 1 asset, or 100 million satoshis for one asset with 8 decimals, and the other decimals in between. The interpretation of the number of decimals is left to the higher level usages.
24  
25  Once created, the assetid is the txid of the create transaction and using the assetid/0 it can spend the assets to however many outputs it creates. The restriction is that the last output must be an opreturn with the assetid. The sum of all but the first output needs to add up to the total assetoshis input. The first output is ignored and used for change from txfee.
26  
27  What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs.
28  
29  There is a special type of transfer to an unspendable address, that locks the asset and creates an offer for all. The details specified in the opreturn. In order to be compatible with the signing restrictions, the "unspendable" address is actually an address whose privkey is known to all. Funds sent to this address can only be spent if the swap parameters are fulfilled, or if the original pubkey cancels the offer by spending it.
30  
31  Types of transactions:
32  create name:description -> txid for assetid
33  transfer <pubkey> <assetid> -> [{address:amount}, ... ] // like withdraw api
34  selloffer <pubkey> <txid/vout> <amount> -> cancel or fillsell -> mempool txid or rejected, might not confirm
35  buyoffer <amount> <assetid> <required> -> cancelbuy or fillbuy -> mempool txid or rejected, might not confirm
36  exchange <pubkey> <txid/vout> <required> <required assetid> -> cancel or fillexchange -> mempool txid or rejected, might not confirm
37  
38  assetsaddress <pubkey> // all assets end up in a special address for each pubkey
39  assetbalance <pubkey> <assetid>
40  assetutxos <pubkey> <assetid>
41  assetsbalances <pubkey>
42  asks <assetid>
43  bids <assetid>
44  swaps <assetid>
45  
46  valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
47  
48  create
49  vin.0: normal input
50  vout.0: issuance assetoshis to CC
51  vout.1: normal output for change (if any)
52  vout.n-1: opreturn [EVAL_ASSETS] ['c'] [origpubkey] "<assetname>" "<description>"
53  
54  transfer
55  vin.0: normal input
56  vin.1 .. vin.n-1: valid CC outputs
57  vout.0 to n-2: assetoshis output to CC
58  vout.n-2: normal output for change (if any)
59  vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
60  
61  buyoffer:
62  vins.*: normal inputs (bid + change)
63  vout.0: amount of bid to unspendable
64  vout.1: normal output for change (if any)
65  vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
66
67  cancelbuy:
68  vin.0: normal input
69  vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
70  vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
71  vout.1: normal output for change (if any)
72  vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
73  
74  fillbuy:
75  vin.0: normal input
76  vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
77  vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
78  vout.0: remaining amount of bid to unspendable
79  vout.1: vin.1 value to signer of vin.2
80  vout.2: vin.2 assetoshis to original pubkey
81  vout.3: CC output for assetoshis change (if any)
82  vout.4: normal output for change (if any)
83  vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
84
85  selloffer:
86  vin.0: normal input
87  vin.1: valid CC output for sale
88  vout.0: vin.1 assetoshis output to CC to unspendable
89  vout.1: CC output for change (if any)
90  vout.2: normal output for change (if any)
91  vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
92  
93  exchange:
94  vin.0: normal input
95  vin.1: valid CC output
96  vout.0: vin.1 assetoshis output to CC to unspendable
97  vout.1: CC output for change (if any)
98  vout.2: normal output for change (if any)
99  vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
100  
101  cancel:
102  vin.0: normal input
103  vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
104  vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
105  vout.1: normal output for change (if any)
106  vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
107  
108  fillsell:
109  vin.0: normal input
110  vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
111  vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
112  vout.0: remaining assetoshis -> unspendable
113  vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
114  vout.2: vin.2 value to original pubkey [origpubkey]
115  vout.3: CC asset for change (if any)
116  vout.4: CC asset2 for change (if any) 'E' only
117  vout.5: normal output for change (if any)
118  vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
119  
120  fillexchange:
121  vin.0: normal input
122  vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0]
123  vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
124  vout.0: remaining assetoshis -> unspendable
125  vout.1: vin.1 assetoshis to signer of vin.2 exchangeTx.vout[0].nValue -> any
126  vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
127  vout.3: normal output for change (if any)
128  vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
129 */
130
131 bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t funcid,uint256 assetid,uint256 assetid2,uint64_t remaining_price,std::vector<uint8_t> origpubkey)
132 {
133     static uint256 zero;
134     CTxDestination address; const CTransaction vinTx; uint256 hashBlock; int32_t i,starti,numvins,preventCCvins,preventCCvouts; uint64_t nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> tmporigpubkey,ignorepubkey; char destaddr[64],origaddr[64],CCaddr[64];
135     fprintf(stderr,"AssetValidate (%c)\n",funcid);
136     numvins = tx.vin.size();
137     outputs = inputs = 0;
138     preventCCvins = preventCCvouts = -1;
139     if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
140         return eval->Invalid("illegal asset vin0");
141     else if ( numvouts < 1 )
142         return eval->Invalid("no vouts");
143     else if ( funcid != 'c' )
144     {
145         if ( funcid == 't' )
146             starti = 0;
147         else starti = 1;
148         if ( assetid == zero )
149             return eval->Invalid("illegal assetid");
150         else if ( AssetExactAmounts(inputs,starti,outputs,eval,tx,assetid) == false )
151             return eval->Invalid("asset inputs != outputs");
152     }
153     switch ( funcid )
154     {
155         case 'c': // create wont be called to be verified as it has no CC inputs
156             //vin.0: normal input
157             //vout.0: issuance assetoshis to CC
158             //vout.1: normal output for change (if any)
159             //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
160             return eval->Invalid("unexpected AssetValidate for createasset");
161             break;
162             
163         case 't': // transfer
164             //vin.0: normal input
165             //vin.1 .. vin.n-1: valid CC outputs
166             //vout.0 to n-2: assetoshis output to CC
167             //vout.n-2: normal output for change (if any)
168             //vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
169             if ( inputs == 0 )
170                 return eval->Invalid("no asset inputs for transfer");
171             fprintf(stderr,"transfer validated %.8f -> %.8f\n",(double)inputs/COIN,(double)outputs/COIN);
172             break;
173             
174         case 'b': // buyoffer
175             //vins.*: normal inputs (bid + change)
176             //vout.0: amount of bid to unspendable
177             //vout.1: normal output for change (if any)
178             // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
179             if ( remaining_price == 0 )
180                 return eval->Invalid("illegal null amount for buyoffer");
181             else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
182                 return eval->Invalid("invalid vout for buyoffer");
183             preventCCvins = 1;
184             preventCCvouts = 1;
185             fprintf(stderr,"buy offer validated to destaddr.(%s)\n",(char *)AssetsCCaddr);
186             break;
187             
188         case 'o': // cancelbuy
189             //vin.0: normal input
190             //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
191             //vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
192             //vout.1: normal output for change (if any)
193             //vout.n-1: opreturn [EVAL_ASSETS] ['o']
194             if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
195                 return(false);
196             else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
197                 return eval->Invalid("invalid refund for cancelbuy");
198             preventCCvins = 2;
199             preventCCvouts = 0;
200             fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
201             break;
202             
203         case 'B': // fillbuy:
204             //vin.0: normal input
205             //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
206             //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
207             //vout.0: remaining amount of bid to unspendable
208             //vout.1: vin.1 value to signer of vin.2
209             //vout.2: vin.2 assetoshis to original pubkey
210             //vout.3: CC output for assetoshis change (if any)
211             //vout.4: normal output for change (if any)
212             //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
213             preventCCvouts = 4;
214             if ( (nValue= AssetValidateBuyvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
215                 return(false);
216             else if ( numvouts < 3 )
217                 return eval->Invalid("not enough vouts for fillbuy");
218             else if ( tmporigpubkey != origpubkey )
219                 return eval->Invalid("mismatched origpubkeys for fillbuy");
220             else
221             {
222                 if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
223                     return eval->Invalid("vout1 is CC for fillbuy");
224                 else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
225                     return eval->Invalid("vout2 is normal for fillbuy");
226                 else if ( ValidateAssetRemainder(0,remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
227                     return eval->Invalid("mismatched remainder for fillbuy");
228                 else if ( remaining_price != 0 )
229                 {
230                     if ( remaining_price < 10000 )
231                         return eval->Invalid("dust vout0 to AssetsCCaddr for fillbuy");
232                     else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
233                         return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
234                 }
235             }
236             fprintf(stderr,"fillbuy validated\n");
237             break;
238             
239         case 's': // selloffer
240         case 'e': // exchange
241             //vin.0: normal input
242             //vin.1: valid CC output for sale
243             //vout.0: vin.1 assetoshis output to CC to unspendable
244             //vout.1: normal output for change (if any)
245             //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
246             //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
247             if ( remaining_price == 0 )
248                 return eval->Invalid("illegal null remaining_price for selloffer");
249             else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
250                 return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
251             preventCCvouts = 1;
252             break;
253             
254         case 'x': // cancel
255             //vin.0: normal input
256             //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
257             //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
258             //vout.1: normal output for change (if any)
259             //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
260             if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
261                 return(false);
262             else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
263                 return eval->Invalid("invalid vout for cancel");
264             preventCCvins = 2;
265             preventCCvouts = 1;
266             break;
267             
268         case 'S': // fillsell
269         case 'E': // fillexchange
270             //vin.0: normal input
271             //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
272             //'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
273             //'E'.vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
274             //vout.0: remaining assetoshis -> unspendable
275             //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
276             //'S'.vout.2: vin.2 value to original pubkey [origpubkey]
277             //'E'.vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
278             //vout.3: normal output for change (if any)
279             //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
280             //'E'.vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
281             if ( funcid == 'E' )
282             {
283                 if ( AssetExactAmounts(inputs,1,outputs,eval,tx,assetid2) == false )
284                     eval->Invalid("asset2 inputs != outputs");
285             }
286             if ( (assetoshis= AssetValidateSellvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
287                 return(false);
288             else if ( numvouts < 3 )
289                 return eval->Invalid("not enough vouts for fill");
290             else if ( tmporigpubkey != origpubkey )
291                 return eval->Invalid("mismatched origpubkeys for fill");
292             else
293             {
294                 if ( ValidateAssetRemainder(1,remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
295                     return eval->Invalid("mismatched remainder for fill");
296                 else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
297                     return eval->Invalid("normal vout1 for fillask");
298                 else if ( funcid == 'E' && ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
299                     return eval->Invalid("normal vout2 for fillask");
300                 else if ( funcid == 'S' && ConstrainVout(tx.vout[2],0,origaddr,0) == 0 )
301                     return eval->Invalid("CC vout2 for fillask");
302                 else if ( remaining_price != 0 )
303                 {
304                     if ( remaining_price < 10000 )
305                         return eval->Invalid("dust vout0 to AssetsCCaddr for fill");
306                     else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
307                         return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
308                 }
309             }
310             fprintf(stderr,"fill validated\n");
311             break;
312     }
313     return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
314 }
315
316 bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
317 {
318     static uint256 zero,prevtxid;
319     CTransaction createTx; uint256 txid,assetid,assetid2,hashBlock; uint8_t funcid; int32_t i,n; uint64_t amount; std::vector<uint8_t> origpubkey;
320     txid = ctx.GetHash();
321     if ( txid == prevtxid )
322         return(true);
323     fprintf(stderr,"ProcessAssets\n");
324     if ( paramsNull.size() != 0 ) // Don't expect params
325         return eval->Invalid("Cannot have params");
326     else if ( (n= ctx.vout.size()) == 0 )
327         return eval->Invalid("no-vouts");
328     else if ( (funcid= DecodeAssetOpRet(ctx.vout[n-1].scriptPubKey,assetid,assetid2,amount,origpubkey)) == 0 )
329         return eval->Invalid("Invalid opreturn payload");
330     else if ( eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
331         return eval->Invalid("cant find asset create txid");
332     else if ( assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
333         return eval->Invalid("cant find asset2 create txid");
334     else if ( AssetValidate(eval,ctx,n,funcid,assetid,assetid2,amount,origpubkey) != 0 )
335     {
336         prevtxid = txid;
337         fprintf(stderr,"AssetValidate.(%c) passed\n",funcid);
338         return(true);
339     }
340     fprintf(stderr,"AssetValidate.(%c) failed\n",funcid);
341     return(false);
342 }
343
This page took 0.043761 seconds and 4 git commands to generate.