]> Git Repo - VerusCoin.git/blame - src/cc/assets.cpp
Fix validate 'o'
[VerusCoin.git] / src / cc / assets.cpp
CommitLineData
f345b953 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
44a9fd7c 16#include "CCassets.h"
325d3231 17
f345b953 18/*
19 Assets can be created or transferred.
20
3bdf31e9 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
ae5ea087 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.
f345b953 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
ae5ea087 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)
5963c84f 52 vout.n-1: opreturn [EVAL_ASSETS] ['c'] [origpubkey] "<assetname>" "<description>"
ae5ea087 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
ae5ea087 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)
325d3231 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)
437d6328 72 vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
ae5ea087 73
325d3231 74 fillbuy:
ae5ea087 75 vin.0: normal input
325d3231 76 vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
143488c8 77 vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
325d3231 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
303d2677 81 vout.3: CC output for assetoshis change (if any)
82 vout.4: normal output for change (if any)
325d3231 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
ae5ea087 88 vout.0: vin.1 assetoshis output to CC to unspendable
143488c8 89 vout.1: CC output for change (if any)
90 vout.2: normal output for change (if any)
325d3231 91 vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
ae5ea087 92
325d3231 93 exchange:
ae5ea087 94 vin.0: normal input
325d3231 95 vin.1: valid CC output
96 vout.0: vin.1 assetoshis output to CC to unspendable
143488c8 97 vout.1: CC output for change (if any)
98 vout.2: normal output for change (if any)
325d3231 99 vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
ae5ea087 100
101 cancel:
102 vin.0: normal input
325d3231 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]
ae5ea087 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
325d3231 110 vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
143488c8 111 vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
325d3231 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]
303d2677 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)
325d3231 118 vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
ae5ea087 119
120 fillexchange:
121 vin.0: normal input
325d3231 122 vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0]
143488c8 123 vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
325d3231 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]
f345b953 129*/
130
8e235d34 131bool 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)
ae5ea087 132{
133 static uint256 zero;
60bcef45 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];
26bb85ca 135 fprintf(stderr,"AssetValidate (%c)\n",funcid);
ae5ea087 136 numvins = tx.vin.size();
325d3231 137 outputs = inputs = 0;
143488c8 138 preventCCvins = preventCCvouts = -1;
b40d83c9 139 if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
ae5ea087 140 return eval->Invalid("illegal asset vin0");
143488c8 141 else if ( numvouts < 1 )
142 return eval->Invalid("no vouts");
143 else if ( funcid != 'c' )
144 {
60bcef45 145 if ( funcid == 't' )
146 starti = 0;
147 else starti = 1;
143488c8 148 if ( assetid == zero )
149 return eval->Invalid("illegal assetid");
60bcef45 150 else if ( AssetExactAmounts(inputs,starti,outputs,eval,tx,assetid) == false )
1d504829 151 return eval->Invalid("asset inputs != outputs");
143488c8 152 }
ae5ea087 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>"}]
325d3231 160 return eval->Invalid("unexpected AssetValidate for createasset");
ae5ea087 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]
325d3231 169 if ( inputs == 0 )
ae5ea087 170 return eval->Invalid("no asset inputs for transfer");
70b25592 171 fprintf(stderr,"transfer validated %.8f -> %.8f\n",(double)inputs/COIN,(double)outputs/COIN);
ae5ea087 172 break;
44a9fd7c 173
ae5ea087 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)
325d3231 178 // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
179 if ( remaining_price == 0 )
ae5ea087 180 return eval->Invalid("illegal null amount for buyoffer");
d082e563 181 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 182 return eval->Invalid("invalid vout for buyoffer");
183 preventCCvins = 1;
184 preventCCvouts = 1;
9b4aec0f 185 fprintf(stderr,"buy offer validated to destaddr.(%s)\n",(char *)AssetsCCaddr);
ae5ea087 186 break;
325d3231 187
ae5ea087 188 case 'o': // cancelbuy
189 //vin.0: normal input
325d3231 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]
ae5ea087 192 //vout.1: normal output for change (if any)
325d3231 193 //vout.n-1: opreturn [EVAL_ASSETS] ['o']
143488c8 194 if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 195 return(false);
d082e563 196 else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
143488c8 197 return eval->Invalid("invalid refund for cancelbuy");
6069e34d 198 preventCCvins = 2;
143488c8 199 preventCCvouts = 0;
6069e34d 200 fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
ae5ea087 201 break;
202
325d3231 203 case 'B': // fillbuy:
ae5ea087 204 //vin.0: normal input
325d3231 205 //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
143488c8 206 //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
325d3231 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
303d2677 210 //vout.3: CC output for assetoshis change (if any)
211 //vout.4: normal output for change (if any)
325d3231 212 //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
143488c8 213 preventCCvouts = 4;
410849f0 214 if ( (nValue= AssetValidateBuyvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 215 return(false);
143488c8 216 else if ( numvouts < 3 )
217 return eval->Invalid("not enough vouts for fillbuy");
325d3231 218 else if ( tmporigpubkey != origpubkey )
219 return eval->Invalid("mismatched origpubkeys for fillbuy");
143488c8 220 else
ae5ea087 221 {
d082e563 222 if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
143488c8 223 return eval->Invalid("vout1 is CC for fillbuy");
d082e563 224 else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
143488c8 225 return eval->Invalid("vout2 is normal for fillbuy");
75b63fac 226 else if ( ValidateAssetRemainder(0,remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
143488c8 227 return eval->Invalid("mismatched remainder for fillbuy");
228 else if ( remaining_price != 0 )
325d3231 229 {
143488c8 230 if ( remaining_price < 10000 )
231 return eval->Invalid("dust vout0 to AssetsCCaddr for fillbuy");
d082e563 232 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 233 return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
234 }
96fc3db2 235 }
70b25592 236 fprintf(stderr,"fillbuy validated\n");
ae5ea087 237 break;
238
325d3231 239 case 's': // selloffer
240 case 'e': // exchange
ae5ea087 241 //vin.0: normal input
325d3231 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");
d082e563 249 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 250 return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
251 preventCCvouts = 1;
ae5ea087 252 break;
44a9fd7c 253
325d3231 254 case 'x': // cancel
ae5ea087 255 //vin.0: normal input
325d3231 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]
143488c8 260 if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 261 return(false);
d082e563 262 else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
143488c8 263 return eval->Invalid("invalid vout for cancel");
264 preventCCvins = 2;
265 preventCCvouts = 1;
ae5ea087 266 break;
325d3231 267
268 case 'S': // fillsell
269 case 'E': // fillexchange
ae5ea087 270 //vin.0: normal input
325d3231 271 //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
143488c8 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
325d3231 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]
143488c8 281 if ( funcid == 'E' )
282 {
d082e563 283 if ( AssetExactAmounts(inputs,outputs,eval,tx,assetid2) == false )
143488c8 284 eval->Invalid("asset2 inputs != outputs");
285 }
410849f0 286 if ( (assetoshis= AssetValidateSellvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 287 return(false);
143488c8 288 else if ( numvouts < 3 )
289 return eval->Invalid("not enough vouts for fill");
325d3231 290 else if ( tmporigpubkey != origpubkey )
143488c8 291 return eval->Invalid("mismatched origpubkeys for fill");
292 else
325d3231 293 {
75b63fac 294 if ( ValidateAssetRemainder(1,remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
143488c8 295 return eval->Invalid("mismatched remainder for fill");
d082e563 296 else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
143488c8 297 return eval->Invalid("normal vout1 for fillask");
d082e563 298 else if ( funcid == 'E' && ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
143488c8 299 return eval->Invalid("normal vout2 for fillask");
d082e563 300 else if ( funcid == 'S' && ConstrainVout(tx.vout[2],0,origaddr,0) == 0 )
143488c8 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");
d082e563 306 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 307 return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
308 }
96fc3db2 309 }
918888fa 310 fprintf(stderr,"fill validated\n");
ae5ea087 311 break;
312 }
463e5af0 313 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
ae5ea087 314}
f345b953 315
badcb332 316bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
f345b953 317{
1d504829 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);
c7814899 323 fprintf(stderr,"ProcessAssets\n");
324 if ( paramsNull.size() != 0 ) // Don't expect params
325 return eval->Invalid("Cannot have params");
e2beb050 326 else if ( (n= ctx.vout.size()) == 0 )
c7814899 327 return eval->Invalid("no-vouts");
e2beb050 328 else if ( (funcid= DecodeAssetOpRet(ctx.vout[n-1].scriptPubKey,assetid,assetid2,amount,origpubkey)) == 0 )
c7814899 329 return eval->Invalid("Invalid opreturn payload");
e2beb050 330 else if ( eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
c7814899 331 return eval->Invalid("cant find asset create txid");
e2beb050 332 else if ( assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
c7814899 333 return eval->Invalid("cant find asset2 create txid");
e2beb050 334 else if ( AssetValidate(eval,ctx,n,funcid,assetid,assetid2,amount,origpubkey) != 0 )
21f17b88 335 {
1d504829 336 prevtxid = txid;
ba94abe8 337 fprintf(stderr,"AssetValidate.(%c) passed\n",funcid);
5a06f2ff 338 return(true);
8e235d34 339 }
ba94abe8 340 fprintf(stderr,"AssetValidate.(%c) failed\n",funcid);
8e235d34 341 return(false);
f345b953 342}
44a9fd7c 343
This page took 0.171769 seconds and 4 git commands to generate.