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 The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
21 This pair of functions are critical to make sure the trading is correct and is the trickiest part of the assets contract.
24 //vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
25 //vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
26 //vout.0: remaining amount of bid to unspendable
27 //vout.1: vin.1 value to signer of vin.2
28 //vout.2: vin.2 assetoshis to original pubkey
29 //vout.3: CC output for assetoshis change (if any)
30 //vout.4: normal output for change (if any)
31 //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
32 ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
34 Yes, this is quite confusing...
36 In ValudateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" 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 totalunits.
38 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.
41 bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits)
43 int64_t unitprice,recvunitprice,newunitprice=0;
44 if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
46 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);
49 else if ( totalunits != (remaining_units + paidunits) )
51 fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits);
54 else if ( orig_nValue != (remaining_nValue + received_nValue) )
56 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);
61 //unitprice = (orig_nValue * COIN) / totalunits;
62 //recvunitprice = (received_nValue * COIN) / paidunits;
63 //if ( remaining_units != 0 )
64 // newunitprice = (remaining_nValue * COIN) / remaining_units;
65 unitprice = (orig_nValue / totalunits);
66 recvunitprice = (received_nValue / paidunits);
67 if ( remaining_units != 0 )
68 newunitprice = (remaining_nValue / remaining_units);
69 if ( recvunitprice < unitprice )
71 fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
74 fprintf(stderr,"orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
79 bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t orig_nValue,int64_t &paidunits,int64_t totalunits)
81 int64_t remaining_nValue,unitprice; double dprice;
82 if ( totalunits == 0 )
84 received_nValue = remaining_units = paidunits = 0;
87 if ( paidunits >= totalunits )
89 paidunits = totalunits;
90 received_nValue = orig_nValue;
92 fprintf(stderr,"totally filled!\n");
95 remaining_units = (totalunits - paidunits);
96 //unitprice = (orig_nValue * COIN) / totalunits;
97 //received_nValue = (paidunits * unitprice) / COIN;
98 unitprice = (orig_nValue / totalunits);
99 received_nValue = (paidunits * unitprice);
100 if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue )
102 remaining_nValue = (orig_nValue - received_nValue);
103 printf("total.%llu - 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);
104 return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits));
105 } else return(false);
108 bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,int64_t orig_assetoshis,int64_t &paid_nValue,int64_t total_nValue)
110 int64_t remaining_assetoshis; double dunitprice;
111 if ( total_nValue == 0 )
113 received_assetoshis = remaining_nValue = paid_nValue = 0;
116 if ( paid_nValue >= total_nValue )
118 paid_nValue = total_nValue;
119 received_assetoshis = orig_assetoshis;
120 remaining_nValue = 0;
121 fprintf(stderr,"totally filled!\n");
124 remaining_nValue = (total_nValue - paid_nValue);
125 dunitprice = ((double)total_nValue / orig_assetoshis);
126 received_assetoshis = (paid_nValue / dunitprice);
127 fprintf(stderr,"remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN);
128 fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
129 if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
131 remaining_assetoshis = (orig_assetoshis - received_assetoshis);
132 return(ValidateAskRemainder(remaining_nValue,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_nValue,total_nValue));
133 } else return(false);
136 bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,int64_t orig_assetoshis,int64_t received_assetoshis,int64_t paid_nValue,int64_t total_nValue)
138 int64_t unitprice,recvunitprice,newunitprice=0;
139 if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 )
141 fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue);
144 else if ( total_nValue != (remaining_nValue + paid_nValue) )
146 fprintf(stderr,"ValidateAssetRemainder: total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue);
149 else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) )
151 fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis);
156 unitprice = (total_nValue / orig_assetoshis);
157 recvunitprice = (paid_nValue / received_assetoshis);
158 if ( remaining_nValue != 0 )
159 newunitprice = (remaining_nValue / remaining_assetoshis);
160 if ( recvunitprice < unitprice )
162 fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
165 fprintf(stderr,"got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
170 bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetoshis2,int64_t orig_assetoshis,int64_t &paid_assetoshis2,int64_t total_assetoshis2)
172 int64_t remaining_assetoshis; double dunitprice;
173 if ( total_assetoshis2 == 0 )
175 fprintf(stderr,"total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2);
176 received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0;
179 if ( paid_assetoshis2 >= total_assetoshis2 )
181 paid_assetoshis2 = total_assetoshis2;
182 received_assetoshis = orig_assetoshis;
183 remaining_assetoshis2 = 0;
184 fprintf(stderr,"totally filled!\n");
187 remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2);
188 dunitprice = ((double)total_assetoshis2 / orig_assetoshis);
189 received_assetoshis = (paid_assetoshis2 / dunitprice);
190 fprintf(stderr,"remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN);
191 fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
192 if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
194 remaining_assetoshis = (orig_assetoshis - received_assetoshis);
195 return(ValidateAskRemainder(remaining_assetoshis2,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_assetoshis2,total_assetoshis2));
196 } else return(false);
199 bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits)
201 int64_t unitprice,recvunitprice,newunitprice=0;
202 if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
204 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);
207 else if ( totalunits != (remaining_price + paidunits) )
209 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);
212 else if ( orig_nValue != (remaining_nValue + received_nValue) )
214 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);
219 unitprice = (orig_nValue * COIN) / totalunits;
220 recvunitprice = (received_nValue * COIN) / paidunits;
221 if ( remaining_price != 0 )
222 newunitprice = (remaining_nValue * COIN) / remaining_price;
223 if ( recvunitprice < unitprice )
225 fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
228 fprintf(stderr,"recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
233 CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
235 CScript opret; uint8_t evalcode = EVAL_ASSETS;
236 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
240 CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey)
242 CScript opret; uint8_t evalcode = EVAL_ASSETS;
243 assetid = revuint256(assetid);
246 case 't': case 'x': case 'o':
247 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid);
249 case 's': case 'b': case 'S': case 'B':
250 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << price << origpubkey);
253 assetid2 = revuint256(assetid2);
254 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << assetid2 << price << origpubkey);
257 fprintf(stderr,"EncodeOpRet: illegal funcid.%02x\n",funcid);
264 bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
266 std::vector<uint8_t> vopret; uint8_t evalcode,funcid,*script;
267 GetOpReturnData(scriptPubKey, vopret);
268 script = (uint8_t *)vopret.data();
269 if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_ASSETS && script[1] == 'c' )
271 if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 )
277 uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey)
279 std::vector<uint8_t> vopret; uint8_t funcid=0,*script,e,f;
280 GetOpReturnData(scriptPubKey, vopret);
281 script = (uint8_t *)vopret.data();
282 memset(&assetid,0,sizeof(assetid));
283 memset(&assetid2,0,sizeof(assetid2));
285 if ( script != 0 && script[0] == EVAL_ASSETS )
288 //fprintf(stderr,"decode.[%c]\n",funcid);
291 case 'c': return(funcid);
293 case 't': case 'x': case 'o':
294 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid) != 0 )
296 assetid = revuint256(assetid);
300 case 's': case 'b': case 'S': case 'B':
301 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> price; ss >> origpubkey) != 0 )
303 assetid = revuint256(assetid);
304 //fprintf(stderr,"got price %llu\n",(long long)price);
309 if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
311 //fprintf(stderr,"got price %llu\n",(long long)price);
312 assetid = revuint256(assetid);
313 assetid2 = revuint256(assetid2);
318 fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n",funcid);
326 bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx)
328 uint256 assetid,assetid2;
329 if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 )
334 bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,const CTransaction& tx)
336 uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector<uint8_t> origpubkey; CScript script;
338 if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
340 if ( GetCCaddress(cp,CCaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 )
345 int64_t IsAssetvout(int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid)
347 uint256 assetid,assetid2; int64_t nValue=0; int32_t n; uint8_t funcid;
348 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) // maybe check address too?
351 nValue = tx.vout[v].nValue;
352 //fprintf(stderr,"CC vout v.%d of n.%d %.8f\n",v,n,(double)nValue/COIN);
355 if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
357 fprintf(stderr,"null decodeopret v.%d\n",v);
360 else if ( funcid == 'c' )
362 if ( refassetid == tx.GetHash() && v == 0 )
365 else if ( (funcid == 'b' || funcid == 'B') && v == 0 ) // critical! 'b'/'B' vout0 is NOT asset
367 else if ( funcid != 'E' )
369 if ( assetid == refassetid )
372 else if ( funcid == 'E' )
374 if ( v < 2 && assetid == refassetid )
376 else if ( v == 2 && assetid2 == refassetid )
380 //fprintf(stderr,"Isassetvout: normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
384 int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
386 uint256 hashBlock; char destaddr[64];
387 origaddr[0] = destaddr[0] = CCaddr[0] = 0;
388 if ( tx.vin.size() < 2 )
389 return eval->Invalid("not enough for CC vins");
390 else if ( tx.vin[vini].prevout.n != 0 )
391 return eval->Invalid("vin1 needs to be buyvin.vout[0]");
392 else if ( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash,vinTx,hashBlock) == 0 )
395 for (z=31; z>=0; z--)
396 fprintf(stderr,"%02x",((uint8_t *)&tx.vin[vini].prevout.hash)[z]);
397 fprintf(stderr," vini.%d\n",vini);
398 return eval->Invalid("always should find CCvin, but didnt");
400 else if ( Getscriptaddress(destaddr,vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || strcmp(destaddr,(char *)cp->unspendableCCaddr) != 0 )
402 fprintf(stderr,"%s vs %s\n",destaddr,(char *)cp->unspendableCCaddr);
403 return eval->Invalid("invalid vin AssetsCCaddr");
405 //else if ( vinTx.vout[0].nValue < 10000 )
406 // return eval->Invalid("invalid dust for buyvin");
407 else if ( GetAssetorigaddrs(cp,CCaddr,origaddr,vinTx) == 0 )
408 return eval->Invalid("couldnt get origaddr for buyvin");
409 fprintf(stderr,"Got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
410 if ( vinTx.vout[0].nValue == 0 )
411 return eval->Invalid("null value CCvin");
412 return(vinTx.vout[0].nValue);
415 int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid)
417 CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid;
418 CCaddr[0] = origaddr[0] = 0;
419 if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
421 else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
422 return eval->Invalid("invalid normal vout0 for buyvin");
425 //fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr);
426 if ( vinTx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' )
427 return eval->Invalid("invalid opreturn for buyvin");
428 else if ( refassetid != assetid )
429 return eval->Invalid("invalid assetid for buyvin");
430 //int32_t i; for (i=31; i>=0; i--)
431 // fprintf(stderr,"%02x",((uint8_t *)&assetid)[i]);
432 //fprintf(stderr," AssetValidateBuyvin assetid for %s\n",origaddr);
437 int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid)
439 CTransaction vinTx; int64_t nValue,assetoshis;
440 fprintf(stderr,"AssetValidateSellvin\n");
441 if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
443 if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 )
444 return eval->Invalid("invalid missing CC vout0 for sellvin");
445 else return(assetoshis);
448 bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid)
450 CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
451 numvins = tx.vin.size();
452 numvouts = tx.vout.size();
453 inputs = outputs = 0;
454 for (i=starti; i<numvins; i++)
456 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
458 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
460 fprintf(stderr,"i.%d starti.%d numvins.%d\n",i,starti,numvins);
461 return eval->Invalid("always should find vin, but didnt");
463 else if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 )
465 fprintf(stderr,"vin%d %llu, ",i,(long long)assetoshis);
466 inputs += assetoshis;
470 for (i=0; i<numvouts; i++)
472 if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,tx,i,assetid)) != 0 )
474 fprintf(stderr,"vout%d %llu, ",i,(long long)assetoshis);
475 outputs += assetoshis;
478 if ( inputs != outputs )
480 fprintf(stderr,"inputs %.8f vs %.8f outputs\n",(double)inputs/COIN,(double)outputs/COIN);