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 ******************************************************************************/
19 Assets can be created or transferred.
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.
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.
25 What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs.
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.
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
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>
44 valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
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>"
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]
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]
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]
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]
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]
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]
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]
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]
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]
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)
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' )
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");
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");
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]
162 return eval->Invalid("no asset inputs for transfer");
163 fprintf(stderr,"transfer validated %.8f -> %.8f\n",(double)inputs/COIN,(double)outputs/COIN);
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");
177 fprintf(stderr,"buy offer validated to destaddr.(%s)\n",(char *)AssetsCCaddr);
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 )
188 else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
189 return eval->Invalid("invalid refund for cancelbuy");
192 fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
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]
205 if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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");
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 )
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");
227 fprintf(stderr,"fillbuy validated\n");
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");
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 )
253 else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
254 return eval->Invalid("invalid vout for cancel");
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]
274 if ( AssetExactAmounts(inputs,outputs,eval,tx,assetid2) == false )
275 eval->Invalid("asset2 inputs != outputs");
277 if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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");
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 )
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");
301 fprintf(stderr,"fill validated\n");
304 if ( preventCCvins >= 0 )
306 for (i=preventCCvins; i<numvins; i++)
308 if ( IsCCInput(tx.vin[i].scriptSig) != 0 )
309 return eval->Invalid("invalid CC vin");
312 if ( preventCCvouts >= 0 )
314 for (i=preventCCvouts; i<numvouts; i++)
316 if ( tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
317 return eval->Invalid("invalid CC vout");
323 bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
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 )
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 )
349 fprintf(stderr,"AssetValidate passed\n");
352 fprintf(stderr,"AssetValidate failed\n");