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