From dda4711a587b6665f6fceba00f683c2768325d26 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 25 Jul 2018 23:08:08 -1100 Subject: [PATCH] Split amounts/validation: bid, ask, swap --- src/cc/CCassets.h | 8 +- src/cc/CCassetsCore.cpp | 160 ++++++++++++++++++++++++++++++++++++---- src/cc/CCassetstx.cpp | 39 +++++----- src/cc/assets.cpp | 73 ++++++++++++++---- 4 files changed, 233 insertions(+), 47 deletions(-) diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 0fbe4891c..3a3df3680 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -35,8 +35,12 @@ CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,uint64_ uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,uint64_t &price,std::vector &origpubkey); bool SetAssetOrigpubkey(std::vector &origpubkey,uint64_t &price,const CTransaction &tx); uint64_t IsAssetvout(uint64_t &price,std::vector &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid); -bool ValidateAssetRemainder(int32_t sellflag,uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidprice,uint64_t totalprice); -bool SetAssetFillamounts(int32_t sellflag,uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice); +bool ValidateBidRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidprice,uint64_t totalprice); +bool ValidateAskRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidprice,uint64_t totalprice); +bool ValidateSwapRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidprice,uint64_t totalprice); +bool SetBidFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice); +bool SetAskFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice); +bool SetSwapFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice); uint64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid); uint64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid); bool AssetExactAmounts(struct CCcontract_info *cp,uint64_t &inputs,int32_t starti,uint64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid); diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index a49da9beb..660479f0c 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -38,7 +38,34 @@ We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. */ -bool ValidateAssetRemainder(int32_t sellflag,uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidunits,uint64_t totalunits) +bool SetBidFillamounts(uint64_t &received_nValue,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &paidunits,uint64_t totalunits) +{ + uint64_t remaining_nValue,unitprice; double dprice; + if ( totalunits == 0 ) + { + received_nValue = remaining_price = paidunits = 0; + return(false); + } + if ( paidunits >= totalunits ) + { + paidunits = totalunits; + received_nValue = orig_nValue; + remaining_price = 0; + fprintf(stderr,"totally filled!\n"); + return(true); + } + remaining_price = (totalunits - paidunits); + unitprice = (orig_nValue * COIN) / totalunits; + received_nValue = (paidunits * unitprice) / COIN; + if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) + { + remaining_nValue = (orig_nValue - received_nValue); + printf("total.%llu 0 paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); + return(ValidateAssetRemainder(sellflag,remaining_price,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); + } else return(false); +} + +bool ValidateBidRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidunits,uint64_t totalunits) { uint64_t unitprice,recvunitprice,newunitprice=0; if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) @@ -72,10 +99,83 @@ bool ValidateAssetRemainder(int32_t sellflag,uint64_t remaining_price,uint64_t r return(true); } -bool SetAssetFillamounts(int32_t sellflag,uint64_t &received_nValue,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &paidunits,uint64_t totalunits) +bool SetAskFillamounts(uint64_t &received_assetoshis,uint64_t &remaining_nValue,uint64_t orig_assetoshis,uint64_t &paid_nValue,uint64_t total_nValue) +{ + uint64_t remaining_nValue,unitprice; double dprice; + if ( totalunits == 0 ) + { + received_nValue = remaining_price = paidunits = 0; + return(false); + } + if ( paidunits >= totalunits ) + { + paidunits = totalunits; + received_nValue = orig_nValue; + remaining_price = 0; + fprintf(stderr,"totally filled!\n"); + return(true); + } + remaining_price = (totalunits - paidunits); + // ./komodo-cli -ac_name=ATEST tokenfillask 9217014eae0a83a0b64632f379c1b474859794f9eaf1cf1eecf5804ed6124a5e ce7811a63e2d06ec6bde8a553b05b7dca95b17e8a676e431a887135ed62549b7 10 + /*{ + "funcid": "s", + "txid": "ce7811a63e2d06ec6bde8a553b05b7dca95b17e8a676e431a887135ed62549b7", + "vout": 0, + "amount": "10", + "askamount": "10", + "origaddress": "RRPpWbVdxcxmhx4xnWnVZFDfGc9p1177ti", + "tokenid": "9217014eae0a83a0b64632f379c1b474859794f9eaf1cf1eecf5804ed6124a5e", + "totalrequired": "1000.00000000", + "price": "100.00000000" + },*/ + dprice = (double)(orig_nValue * COIN) / totalunits; + received_nValue = (paidunits * dprice); + fprintf(stderr,"dprice %.8f orig %llu/total %llu, recv %llu\n",dprice,(long long)orig_nValue,(long long)totalunits,(long long)received_nValue); + if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) + { + remaining_nValue = (orig_nValue - received_nValue); + printf("total.%llu 0 paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); + return(ValidateAssetRemainder(sellflag,remaining_price,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); + } else return(false); +} + +bool ValidateAskRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidunits,uint64_t totalunits) +{ + uint64_t unitprice,recvunitprice,newunitprice=0; + if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) + { + fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + return(false); + } + else if ( totalunits != (remaining_price + paidunits) ) + { + fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); + return(false); + } + else if ( orig_nValue != (remaining_nValue + received_nValue) ) + { + fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + return(false); + } + else + { + unitprice = (orig_nValue * COIN) / totalunits; + recvunitprice = (received_nValue * COIN) / paidunits; + if ( remaining_price != 0 ) + newunitprice = (remaining_nValue * COIN) / remaining_price; + if ( recvunitprice < unitprice ) + { + fprintf(stderr,"error recvunitprice %.16f < %.16f unitprice, new unitprice %.16f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + return(false); + } + fprintf(stderr,"recvunitprice %.16f >= %.16f unitprice, new unitprice %.16f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + } + return(true); +} + +bool SetSwapFillamounts(int32_t sellflag,uint64_t &received_nValue,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &paidunits,uint64_t totalunits) { uint64_t remaining_nValue,unitprice; double dprice; - if ( totalunits == 0 ) { received_nValue = remaining_price = paidunits = 0; @@ -99,16 +199,16 @@ bool SetAssetFillamounts(int32_t sellflag,uint64_t &received_nValue,uint64_t &re { // ./komodo-cli -ac_name=ATEST tokenfillask 9217014eae0a83a0b64632f379c1b474859794f9eaf1cf1eecf5804ed6124a5e ce7811a63e2d06ec6bde8a553b05b7dca95b17e8a676e431a887135ed62549b7 10 /*{ - "funcid": "s", - "txid": "ce7811a63e2d06ec6bde8a553b05b7dca95b17e8a676e431a887135ed62549b7", - "vout": 0, - "amount": "10", - "askamount": "10", - "origaddress": "RRPpWbVdxcxmhx4xnWnVZFDfGc9p1177ti", - "tokenid": "9217014eae0a83a0b64632f379c1b474859794f9eaf1cf1eecf5804ed6124a5e", - "totalrequired": "1000.00000000", - "price": "100.00000000" - },*/ + "funcid": "s", + "txid": "ce7811a63e2d06ec6bde8a553b05b7dca95b17e8a676e431a887135ed62549b7", + "vout": 0, + "amount": "10", + "askamount": "10", + "origaddress": "RRPpWbVdxcxmhx4xnWnVZFDfGc9p1177ti", + "tokenid": "9217014eae0a83a0b64632f379c1b474859794f9eaf1cf1eecf5804ed6124a5e", + "totalrequired": "1000.00000000", + "price": "100.00000000" + },*/ dprice = (double)(orig_nValue * COIN) / totalunits; received_nValue = (paidunits * dprice); fprintf(stderr,"dprice %.8f orig %llu/total %llu, recv %llu\n",dprice,(long long)orig_nValue,(long long)totalunits,(long long)received_nValue); @@ -121,6 +221,40 @@ bool SetAssetFillamounts(int32_t sellflag,uint64_t &received_nValue,uint64_t &re } else return(false); } +bool ValidateSwapRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidunits,uint64_t totalunits) +{ + uint64_t unitprice,recvunitprice,newunitprice=0; + if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) + { + fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + return(false); + } + else if ( totalunits != (remaining_price + paidunits) ) + { + fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); + return(false); + } + else if ( orig_nValue != (remaining_nValue + received_nValue) ) + { + fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + return(false); + } + else + { + unitprice = (orig_nValue * COIN) / totalunits; + recvunitprice = (received_nValue * COIN) / paidunits; + if ( remaining_price != 0 ) + newunitprice = (remaining_nValue * COIN) / remaining_price; + if ( recvunitprice < unitprice ) + { + fprintf(stderr,"error recvunitprice %.16f < %.16f unitprice, new unitprice %.16f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + return(false); + } + fprintf(stderr,"recvunitprice %.16f >= %.16f unitprice, new unitprice %.16f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + } + return(true); +} + CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description) { CScript opret; uint8_t evalcode = EVAL_ASSETS; diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 0c0b0923d..567dce3f0 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -279,7 +279,9 @@ std::string FillBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid,uint64_t mtx.vin.push_back(CTxIn(bidtxid,bidvout,CScript())); if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,fillamount,60)) > 0 ) { - SetAssetFillamounts(0,paid_amount,remaining_required,bidamount,fillamount,origprice); + if ( inputs < fillamount ) + fillamount = inputs; + SetBidFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice); if ( inputs > fillamount ) CCchange = (inputs - fillamount); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount - paid_amount,GetUnspendable(cp,0))); @@ -295,9 +297,9 @@ std::string FillBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid,uint64_t return("no normal coins left"); } -std::string FillSell(uint64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,uint64_t fillamount) +std::string FillSell(uint64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,uint64_t paid_nValue) { - CTransaction vintx,filltx; uint256 hashBlock; CMutableTransaction mtx; CPubKey mypk; std::vector origpubkey; int32_t askvout=0; uint64_t totalunits,askamount,paid_amount,remaining_required,inputs,CCchange=0; struct CCcontract_info *cp,C; + CTransaction vintx,filltx; uint256 hashBlock; CMutableTransaction mtx; CPubKey mypk; std::vector origpubkey; int32_t askvout=0; uint64_t received_assetoshis,total_nValue,orig_assetoshis,paid_nValue,remaining_nValue,inputs,CCchange=0; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_ASSETS); if ( txfee == 0 ) txfee = 10000; @@ -306,28 +308,27 @@ std::string FillSell(uint64_t txfee,uint256 assetid,uint256 assetid2,uint256 ask { if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 ) { - askamount = vintx.vout[askvout].nValue; - SetAssetOrigpubkey(origpubkey,totalunits,vintx); + orig_assetoshis = vintx.vout[askvout].nValue; + SetAssetOrigpubkey(origpubkey,total_nValue,vintx); mtx.vin.push_back(CTxIn(asktxid,askvout,CScript())); if ( assetid2 != zeroid ) - inputs = AddAssetInputs(cp,mtx,mypk,assetid2,fillamount,60); - else inputs = AddNormalinputs(mtx,mypk,fillamount,60); + inputs = AddAssetInputs(cp,mtx,mypk,assetid2,paid_nValue,60); + else inputs = AddNormalinputs(mtx,mypk,paid_nValue,60); if ( inputs > 0 ) { - if ( inputs < fillamount ) - fillamount = inputs; - fprintf(stderr,"inputs %llu, fillamount.%llu\n",(long long)inputs,(long long)fillamount); - SetAssetFillamounts(1,paid_amount,remaining_required,askamount,fillamount,totalunits); - fprintf(stderr,"paidamount %llu remain %llu, fill %llu\n",(long long)paid_amount,(long long)remaining_required,(long long)fillamount); - if ( assetid2 != zeroid && inputs > fillamount ) - CCchange = (inputs - fillamount); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount - paid_amount,GetUnspendable(cp,0))); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,paid_amount,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,fillamount,pubkey2pk(origpubkey))); + if ( inputs < paid_nValue ) + paid_nValue = inputs; + if ( assetid2 != zeroid ) + SetSwapFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue); + else SetAskFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue); + if ( assetid2 != zeroid && inputs > paid_nValue ) + CCchange = (inputs - paid_nValue); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,orig_assetoshis - received_assetoshis,GetUnspendable(cp,0))); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,received_assetoshis,mypk)); + mtx.vout.push_back(CTxOut(paid_nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); - fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required); - return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeAssetOpRet(assetid2!=zeroid?'E':'S',assetid,assetid2,remaining_required,origpubkey))); + return(FinalizeCCTx(cp,mtx,mypk,txfee,EncodeAssetOpRet(assetid2!=zeroid?'E':'S',assetid,assetid2,remaining_nValue,origpubkey))); } else fprintf(stderr,"filltx not enough utxos\n"); } } diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 1aea32e00..ee153b6f9 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -227,11 +227,17 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx return eval->Invalid("mismatched origpubkeys for fillbuy"); else { - if ( ConstrainVout(tx.vout[1],0,0,0) == 0 ) - return eval->Invalid("vout1 is CC for fillbuy"); - else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 ) + inputs = 0; + for (i=2; iInvalid("vout2 is normal for fillbuy"); - else if ( ValidateAssetRemainder(0,remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false ) + else if ( ConstrainVout(tx.vout[1],0,0,0) == 0 ) + return eval->Invalid("vout1 is CC for fillbuy"); + else if ( ValidateBidRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,inputs,totalunits) == false ) return eval->Invalid("mismatched remainder for fillbuy"); else if ( remaining_price != 0 ) { @@ -273,6 +279,45 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx break; case 'S': // fillsell + //vin.0: normal input + //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] + //'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue + //vout.0: remaining assetoshis -> unspendable + //vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any + //'S'.vout.2: vin.2 value to original pubkey [origpubkey] + //vout.3: normal output for change (if any) + //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] + if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 ) + return(false); + else if ( numvouts < 3 ) + return eval->Invalid("not enough vouts for fill"); + else if ( tmporigpubkey != origpubkey ) + return eval->Invalid("mismatched origpubkeys for fill"); + else + { + inputs = 0; + for (i=2; i orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits); + if ( ValidateAskRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[2].nValue,tx.vout[1].nValue,totalunits) == false ) + return eval->Invalid("mismatched remainder for fill"); + else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 ) + return eval->Invalid("normal vout1 for fillask"); + else if ( ConstrainVout(tx.vout[2],0,origaddr,0) == 0 ) + return eval->Invalid("CC vout2 for fillask"); + else if ( remaining_price != 0 ) + { + if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 ) + return eval->Invalid("mismatched vout0 AssetsCCaddr for fill"); + } + } + fprintf(stderr,"fill validated\n"); + break; case 'E': // fillexchange //vin.0: normal input //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] @@ -285,11 +330,8 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] //'E'.vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] - if ( funcid == 'E' ) - { - if ( AssetExactAmounts(cp,inputs,1,outputs,eval,tx,assetid2) == false ) - eval->Invalid("asset2 inputs != outputs"); - } + if ( AssetExactAmounts(cp,inputs,1,outputs,eval,tx,assetid2) == false ) + eval->Invalid("asset2 inputs != outputs"); if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 ) return(false); else if ( numvouts < 3 ) @@ -298,16 +340,21 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx return eval->Invalid("mismatched origpubkeys for fill"); else { + inputs = 0; + for (i=2; i orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits); + if ( ValidateSwapRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[2].nValue,tx.vout[1].nValue,totalunits) == false ) return eval->Invalid("mismatched remainder for fill"); else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 ) return eval->Invalid("normal vout1 for fillask"); - else if ( funcid == 'E' && ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 ) + else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 ) return eval->Invalid("normal vout2 for fillask"); - else if ( funcid == 'S' && ConstrainVout(tx.vout[2],0,origaddr,0) == 0 ) - return eval->Invalid("CC vout2 for fillask"); else if ( remaining_price != 0 ) { if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )