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 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.
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.
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.
27 What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs.
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.
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
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>
46 valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
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>"
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]
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]
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]
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]
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]
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]
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]
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]
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]
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)
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' )
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");
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");
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]
170 return eval->Invalid("no asset inputs for transfer");
171 fprintf(stderr,"transfer validated %.8f -> %.8f\n",(double)inputs/COIN,(double)outputs/COIN);
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");
185 fprintf(stderr,"buy offer validated to destaddr.(%s)\n",(char *)AssetsCCaddr);
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 )
196 else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
197 return eval->Invalid("invalid refund for cancelbuy");
200 fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
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]
214 if ( (nValue= AssetValidateBuyvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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");
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 )
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");
236 fprintf(stderr,"fillbuy validated\n");
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");
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 )
262 else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
263 return eval->Invalid("invalid vout for cancel");
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]
283 if ( AssetExactAmounts(inputs,1,outputs,eval,tx,assetid2) == false )
284 eval->Invalid("asset2 inputs != outputs");
286 if ( (assetoshis= AssetValidateSellvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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");
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 )
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");
310 fprintf(stderr,"fill validated\n");
313 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
316 bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
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 )
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 )
337 fprintf(stderr,"AssetValidate.(%c) passed\n",funcid);
340 fprintf(stderr,"AssetValidate.(%c) failed\n",funcid);