]> Git Repo - VerusCoin.git/blame - src/cc/assets.cpp
Fix fillsell
[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;
410849f0 134 CTxDestination address; const CTransaction vinTx; uint256 hashBlock; int32_t i,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 {
145 if ( assetid == zero )
146 return eval->Invalid("illegal assetid");
d082e563 147 else if ( AssetExactAmounts(inputs,outputs,eval,tx,assetid) == false )
1d504829 148 return eval->Invalid("asset inputs != outputs");
143488c8 149 }
ae5ea087 150 switch ( funcid )
151 {
152 case 'c': // create wont be called to be verified as it has no CC inputs
153 //vin.0: normal input
154 //vout.0: issuance assetoshis to CC
155 //vout.1: normal output for change (if any)
156 //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
325d3231 157 return eval->Invalid("unexpected AssetValidate for createasset");
ae5ea087 158 break;
159
160 case 't': // transfer
161 //vin.0: normal input
162 //vin.1 .. vin.n-1: valid CC outputs
163 //vout.0 to n-2: assetoshis output to CC
164 //vout.n-2: normal output for change (if any)
165 //vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
325d3231 166 if ( inputs == 0 )
ae5ea087 167 return eval->Invalid("no asset inputs for transfer");
70b25592 168 fprintf(stderr,"transfer validated %.8f -> %.8f\n",(double)inputs/COIN,(double)outputs/COIN);
ae5ea087 169 break;
44a9fd7c 170
ae5ea087 171 case 'b': // buyoffer
172 //vins.*: normal inputs (bid + change)
173 //vout.0: amount of bid to unspendable
174 //vout.1: normal output for change (if any)
325d3231 175 // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
176 if ( remaining_price == 0 )
ae5ea087 177 return eval->Invalid("illegal null amount for buyoffer");
d082e563 178 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 179 return eval->Invalid("invalid vout for buyoffer");
180 preventCCvins = 1;
181 preventCCvouts = 1;
9b4aec0f 182 fprintf(stderr,"buy offer validated to destaddr.(%s)\n",(char *)AssetsCCaddr);
ae5ea087 183 break;
325d3231 184
ae5ea087 185 case 'o': // cancelbuy
186 //vin.0: normal input
325d3231 187 //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
188 //vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
ae5ea087 189 //vout.1: normal output for change (if any)
325d3231 190 //vout.n-1: opreturn [EVAL_ASSETS] ['o']
143488c8 191 if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 192 return(false);
d082e563 193 else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
143488c8 194 return eval->Invalid("invalid refund for cancelbuy");
6069e34d 195 preventCCvins = 2;
143488c8 196 preventCCvouts = 0;
6069e34d 197 fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
ae5ea087 198 break;
199
325d3231 200 case 'B': // fillbuy:
ae5ea087 201 //vin.0: normal input
325d3231 202 //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
143488c8 203 //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
325d3231 204 //vout.0: remaining amount of bid to unspendable
205 //vout.1: vin.1 value to signer of vin.2
206 //vout.2: vin.2 assetoshis to original pubkey
303d2677 207 //vout.3: CC output for assetoshis change (if any)
208 //vout.4: normal output for change (if any)
325d3231 209 //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
143488c8 210 preventCCvouts = 4;
410849f0 211 if ( (nValue= AssetValidateBuyvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 212 return(false);
143488c8 213 else if ( numvouts < 3 )
214 return eval->Invalid("not enough vouts for fillbuy");
325d3231 215 else if ( tmporigpubkey != origpubkey )
216 return eval->Invalid("mismatched origpubkeys for fillbuy");
143488c8 217 else
ae5ea087 218 {
d082e563 219 if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
143488c8 220 return eval->Invalid("vout1 is CC for fillbuy");
d082e563 221 else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
143488c8 222 return eval->Invalid("vout2 is normal for fillbuy");
410849f0 223 else if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
143488c8 224 return eval->Invalid("mismatched remainder for fillbuy");
225 else if ( remaining_price != 0 )
325d3231 226 {
143488c8 227 if ( remaining_price < 10000 )
228 return eval->Invalid("dust vout0 to AssetsCCaddr for fillbuy");
d082e563 229 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 230 return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
231 }
96fc3db2 232 }
70b25592 233 fprintf(stderr,"fillbuy validated\n");
ae5ea087 234 break;
235
325d3231 236 case 's': // selloffer
237 case 'e': // exchange
ae5ea087 238 //vin.0: normal input
325d3231 239 //vin.1: valid CC output for sale
240 //vout.0: vin.1 assetoshis output to CC to unspendable
241 //vout.1: normal output for change (if any)
242 //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
243 //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
244 if ( remaining_price == 0 )
245 return eval->Invalid("illegal null remaining_price for selloffer");
d082e563 246 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 247 return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
248 preventCCvouts = 1;
ae5ea087 249 break;
44a9fd7c 250
325d3231 251 case 'x': // cancel
ae5ea087 252 //vin.0: normal input
325d3231 253 //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
254 //vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
255 //vout.1: normal output for change (if any)
256 //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
143488c8 257 if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 258 return(false);
d082e563 259 else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
143488c8 260 return eval->Invalid("invalid vout for cancel");
261 preventCCvins = 2;
262 preventCCvouts = 1;
ae5ea087 263 break;
325d3231 264
265 case 'S': // fillsell
266 case 'E': // fillexchange
ae5ea087 267 //vin.0: normal input
325d3231 268 //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
143488c8 269 //'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
270 //'E'.vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
325d3231 271 //vout.0: remaining assetoshis -> unspendable
272 //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
273 //'S'.vout.2: vin.2 value to original pubkey [origpubkey]
274 //'E'.vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
275 //vout.3: normal output for change (if any)
276 //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
277 //'E'.vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
143488c8 278 if ( funcid == 'E' )
279 {
d082e563 280 if ( AssetExactAmounts(inputs,outputs,eval,tx,assetid2) == false )
143488c8 281 eval->Invalid("asset2 inputs != outputs");
282 }
410849f0 283 if ( (assetoshis= AssetValidateSellvin(eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
325d3231 284 return(false);
143488c8 285 else if ( numvouts < 3 )
286 return eval->Invalid("not enough vouts for fill");
325d3231 287 else if ( tmporigpubkey != origpubkey )
143488c8 288 return eval->Invalid("mismatched origpubkeys for fill");
289 else
325d3231 290 {
efa4ed1f 291 // if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
292 //bool ValidateAssetRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidunits,uint64_t totalunits)
293
294
fc6ac432 295 if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
143488c8 296 return eval->Invalid("mismatched remainder for fill");
d082e563 297 else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
143488c8 298 return eval->Invalid("normal vout1 for fillask");
d082e563 299 else if ( funcid == 'E' && ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
143488c8 300 return eval->Invalid("normal vout2 for fillask");
d082e563 301 else if ( funcid == 'S' && ConstrainVout(tx.vout[2],0,origaddr,0) == 0 )
143488c8 302 return eval->Invalid("CC vout2 for fillask");
303 else if ( remaining_price != 0 )
304 {
305 if ( remaining_price < 10000 )
306 return eval->Invalid("dust vout0 to AssetsCCaddr for fill");
d082e563 307 else if ( ConstrainVout(tx.vout[0],1,(char *)AssetsCCaddr,0) == 0 )
143488c8 308 return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
309 }
96fc3db2 310 }
918888fa 311 fprintf(stderr,"fill validated\n");
ae5ea087 312 break;
313 }
463e5af0 314 return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
ae5ea087 315}
f345b953 316
badcb332 317bool ProcessAssets(Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
f345b953 318{
1d504829 319 static uint256 zero,prevtxid;
320 CTransaction createTx; uint256 txid,assetid,assetid2,hashBlock; uint8_t funcid; int32_t i,n; uint64_t amount; std::vector<uint8_t> origpubkey;
321 txid = ctx.GetHash();
322 if ( txid == prevtxid )
323 return(true);
c7814899 324 fprintf(stderr,"ProcessAssets\n");
325 if ( paramsNull.size() != 0 ) // Don't expect params
326 return eval->Invalid("Cannot have params");
e2beb050 327 else if ( (n= ctx.vout.size()) == 0 )
c7814899 328 return eval->Invalid("no-vouts");
e2beb050 329 else if ( (funcid= DecodeAssetOpRet(ctx.vout[n-1].scriptPubKey,assetid,assetid2,amount,origpubkey)) == 0 )
c7814899 330 return eval->Invalid("Invalid opreturn payload");
e2beb050 331 else if ( eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
c7814899 332 return eval->Invalid("cant find asset create txid");
e2beb050 333 else if ( assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
c7814899 334 return eval->Invalid("cant find asset2 create txid");
e2beb050 335 else if ( AssetValidate(eval,ctx,n,funcid,assetid,assetid2,amount,origpubkey) != 0 )
21f17b88 336 {
1d504829 337 prevtxid = txid;
ba94abe8 338 fprintf(stderr,"AssetValidate.(%c) passed\n",funcid);
5a06f2ff 339 return(true);
8e235d34 340 }
ba94abe8 341 fprintf(stderr,"AssetValidate.(%c) failed\n",funcid);
8e235d34 342 return(false);
f345b953 343}
44a9fd7c 344
This page took 0.167361 seconds and 4 git commands to generate.