Fix remainder and validation of prices

This commit is contained in:
jl777
2018-07-22 23:40:16 -11:00
parent 115a38af5b
commit 303d26770f
2 changed files with 98 additions and 59 deletions

View File

@@ -15,6 +15,92 @@
#include "CCassets.h"
/*
The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
This pair of functions are critical to make sure the trading is correct and is the trickiest part of the assets contract.
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
//vout.0: remaining amount of bid to unspendable
//vout.1: vin.1 value to signer of vin.2
//vout.2: vin.2 assetoshis to original pubkey
//vout.3: CC output for assetoshis change (if any)
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalprice);
Yes, this is quite confusing...
In ValudateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "price" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined pricetotal.
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(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received_nValue,uint64_t paidprice,uint64_t totalprice)
{
uint64_t unitprice,recvunitprice,newunitprice=0;
if ( orig_nValue == 0 || received == 0 || paid == 0 || totalprice == 0 )
{
fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received == %llu || paid == %llu || totalprice == %llu\n",(long long)orig_nValue,(long long)received,(long long)paid,(long long)totalprice);
return(false);
}
else if ( totalprice != (remaining_price + paidprice) )
{
fprintf(stderr,"ValidateAssetRemainder: totalprice %llu != %llu (remaining_price %llu + %llu paidprice)\n",(long long)totalprice,(long long)(remaining_price + paidprice),(long long)remaining_price,(long long)paidprice);
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) / totalprice;
recvunitprice = (received_nValue * COIN) / paidprice;
if ( recvunitprice < unitprice )
{
fprintf(stderr,"recvunitprice %llu < %llu unitprice\n",(long long)recvunitprice,(long long)unitprice);
return(false);
}
if ( remaining_price != 0 )
newunitprice = (remaining_nValue * COIN) / remaining_price;
fprintf(stderr,"recvunitprice %llu >= %llu unitprice, new unitprice %llu\n",(long long)recvunitprice,(long long)unitprice,(long long)newunitprice);
}
return(true);
}
/*
SetAssetFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice);
*/
bool SetAssetFillamounts(uint64_t &received_nValue,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &paidprice,uint64_t totalprice)
{
uint64_t remaining_nValue,unitprice;
if ( totalprice == 0 )
{
received_nValue = remaining_price = paidprice = 0;
return(false);
}
if ( paidprice >= totalprice )
{
paidprice = totalprice;
received_nValue = orig_nValue;
remaining_price = 0;
fprint(stderr,"totally filled!\n");
return(true);
}
remaining_price = (totalprice - paidprice);
unitprice = (orig_nValue * COIN) / totalprice;
if ( unitprice > 0 && (received_nValue= paidprice * unitprice) > 0 && received_nValue < orig_nValue )
{
remaining_nValue = (orig_nValue - received_nValue);
return(ValidateAssetRemainder(remaining_price,remaining_nValue,orig_nValue,received_nValue,paidprice,totalprice));
} else return(false);
}
CC *MakeAssetCond(CPubKey pk)
{
std::vector<CC*> pks; uint8_t evalcode = EVAL_ASSETS;
@@ -171,57 +257,6 @@ uint64_t IsAssetvout(uint64_t &price,std::vector<uint8_t> &origpubkey,const CTra
return(0);
}
bool ValidateAssetRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received,int64_t paid,int64_t totalprice)
{
uint64_t price,recvprice;
if ( received >= totalprice )
received = totalprice;
if ( orig_nValue == 0 || received == 0 || paid == 0 || totalprice == 0 )
{
fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received == %llu || paid == %llu || totalprice == %llu\n",(long long)orig_nValue,(long long)received,(long long)paid,(long long)totalprice);
return(false);
}
else if ( remaining_price < (totalprice - received) )
{
fprintf(stderr,"ValidateAssetRemainder: remaining_price %llu < %llu (totalprice %llu - %llu received)\n",(long long)remaining_price,(long long)(totalprice - received),(long long)totalprice,(long long)received);
return(false);
}
else if ( remaining_nValue < (orig_nValue - paid) )
{
fprintf(stderr,"ValidateAssetRemainder: remaining_nValue %llu < %llu (totalprice %llu - %llu received)\n",(long long)remaining_nValue,(long long)(orig_nValue - paid),(long long)orig_nValue,(long long)paid);
return(false);
}
else if ( remaining_nValue > 0 )
{
price = (totalprice * COIN) / orig_nValue;
recvprice = (received * COIN) / paid;
if ( recvprice < price )
{
fprintf(stderr,"recvprice %llu < %llu price\n",(long long)recvprice,(long long)price);
return(false);
}
}
return(true);
}
bool SetAssetFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice)
{
uint64_t remaining_nValue,price,mult;
if ( received >= totalprice )
received = totalprice;
remaining_price = (totalprice - received);
price = (totalprice * COIN) / orig_nValue;
mult = (received * COIN);
fprintf(stderr,"remaining %llu price %llu, mult %llu, totalprice %llu, received %llu, paid %llu\n",(long long)remaining_price,(long long)price,(long long)mult,(long long)totalprice,(long long)received,(long long)mult / price);
if ( price > 0 && (paid= mult / price) > 0 )
{
if ( (mult % price) != 0 )
paid--;
remaining_nValue = (orig_nValue - paid);
return(ValidateAssetRemainder(remaining_price,remaining_nValue,orig_nValue,received,paid,totalprice));
} else return(false);
}
uint64_t AssetValidateCCvin(Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,CTransaction &vinTx)
{
uint256 hashBlock; char destaddr[64];

View File

@@ -76,7 +76,8 @@
vout.0: remaining amount of bid to unspendable
vout.1: vin.1 value to signer of vin.2
vout.2: vin.2 assetoshis to original pubkey
vout.3: normal output for change (if any)
vout.3: CC output for assetoshis change (if any)
vout.4: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
selloffer:
@@ -109,7 +110,9 @@
vout.0: remaining assetoshis -> unspendable
vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
vout.2: vin.2 value to original pubkey [origpubkey]
vout.3: normal output for change (if any)
vout.3: CC asset for change (if any)
vout.4: CC asset2 for change (if any) 'E' only
vout.5: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
fillexchange:
@@ -126,7 +129,7 @@
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)
{
static uint256 zero;
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];
CTxDestination address; const CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,preventCCvins,preventCCvouts; uint64_t nValue,assetoshis,outputs,inputs,tmpprice,totalprice,ignore; std::vector<uint8_t> tmporigpubkey,ignorepubkey; char destaddr[64],origaddr[64],CCaddr[64];
fprintf(stderr,"AssetValidate (%c)\n",funcid);
numvins = tx.vin.size();
outputs = inputs = 0;
@@ -199,10 +202,11 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
//vout.0: remaining amount of bid to unspendable
//vout.1: vin.1 value to signer of vin.2
//vout.2: vin.2 assetoshis to original pubkey
//vout.3: normal output for change (if any)
//vout.3: CC output for assetoshis change (if any)
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
preventCCvouts = 4;
if ( (nValue= AssetValidateBuyvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if ( (nValue= AssetValidateBuyvin(eval,totalprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
return eval->Invalid("not enough vouts for fillbuy");
@@ -214,7 +218,7 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
return eval->Invalid("vout1 is CC for fillbuy");
else if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
return eval->Invalid("vout2 is normal for fillbuy");
else if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,tmpprice) == false )
else if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalprice) == false )
return eval->Invalid("mismatched remainder for fillbuy");
else if ( remaining_price != 0 )
{
@@ -274,7 +278,7 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
if ( AssetExactAmounts(inputs,outputs,eval,tx,assetid2) == false )
eval->Invalid("asset2 inputs != outputs");
}
if ( (assetoshis= AssetValidateSellvin(eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if ( (assetoshis= AssetValidateSellvin(eval,totalprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
return eval->Invalid("not enough vouts for fill");
@@ -282,7 +286,7 @@ bool AssetValidate(Eval* eval,const CTransaction &tx,int32_t numvouts,uint8_t fu
return eval->Invalid("mismatched origpubkeys for fill");
else
{
if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,tmpprice) == false )
if ( ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalprice) == 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");