Browse Source

Split Asset contract into Tokens and Assets

metaverse
dimxy 6 years ago
parent
commit
8e8a94404f
  1. 1
      src/Makefile.am
  2. 25
      src/cc/CCHeir.h
  3. 22
      src/cc/CCassets.h
  4. 403
      src/cc/CCassetsCore.cpp
  5. 435
      src/cc/CCassetstx.cpp
  6. 22
      src/cc/CCcustom.cpp
  7. 17
      src/cc/CCinclude.h
  8. 487
      src/cc/CCtokens.cpp
  9. 76
      src/cc/CCtokens.h
  10. 13
      src/cc/CCtx.cpp
  11. 167
      src/cc/assets.cpp
  12. 3
      src/cc/eval.h
  13. 1430
      src/cc/heir.cpp
  14. 622
      src/cc/heir_validate.h
  15. 15
      src/rpc/server.cpp
  16. 9
      src/rpc/server.h
  17. 441
      src/wallet/rpcwallet.cpp

1
src/Makefile.am

@ -290,6 +290,7 @@ libbitcoin_server_a_SOURCES = \
cc/CCcustom.cpp \
cc/CCtx.cpp \
cc/CCutils.cpp \
cc/CCtokens.cpp \
cc/assets.cpp \
cc/faucet.cpp \
cc/rewards.cpp \

25
src/cc/CCHeir.h

@ -18,12 +18,33 @@
#define CC_HEIR_H
#include "CCinclude.h"
#include "CCtokens.h"
#define EVAL_HEIR 0xea
//#define EVAL_HEIR 0xea
bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
class CoinHelper;
class TokenHelper;
// CCcustom
UniValue HeirInfo();
// this would not link
//template <class Helper> std::string HeirFund(uint64_t txfee,int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 assetid);
//template <class Helper> UniValue HeirAdd(uint256 fundingtxid, uint64_t txfee, int64_t amount);
//template <class Helper> UniValue HeirClaim(uint256 fundingtxid, uint64_t txfee, int64_t nValue);
std::string HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 assetid);
std::string HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 assetid);
UniValue HeirClaimCoinCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount);
UniValue HeirClaimTokenCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount);
UniValue HeirAddCoinCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount);
UniValue HeirAddTokenCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount);
UniValue HeirInfo(uint256 fundingtxid);
UniValue HeirList();
//std::string Heir_MakeBadTx(uint256 fundingtxid, uint8_t funcId, int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTime, uint32_t errMask);
//bool HeirExactTokenAmounts(bool compareTotals, struct CCcontract_info *cpHeir, Eval* eval, uint256 assetid, const CTransaction &tx);
#endif

22
src/cc/CCassets.h

@ -30,12 +30,12 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
// CCassetsCore
//CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk);
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
//CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description);
//CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx);
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid);
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid);
bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
@ -44,18 +44,18 @@ bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValu
bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
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);
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);
bool AssetExactAmounts(int32_t maxDepth, struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid);
//bool AssetExactAmounts(bool doValidateTx, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid, std::vector<CTransaction> &ccVinsTxs);
bool AssetExactAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid);
// CCassetstx
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid);
int64_t AddAssetInputs(CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs);
UniValue AssetOrders(uint256 tokenid);
UniValue AssetInfo(uint256 tokenid);
UniValue AssetList();
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
//std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
//std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
//std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal);
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid);

403
src/cc/CCassetsCore.cpp

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* Copyright © 2014-2018 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
@ -33,7 +33,7 @@
Yes, this is quite confusing...
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.
In ValidateAssetRemainder 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.
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.
*/
@ -230,38 +230,45 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6
return(true);
}
/* use EncodeTokenCreateOpRet instead:
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
return(opret);
}
*/
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey)
CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
assetid = revuint256(assetid);
switch ( funcid )
CScript opret;
uint8_t evalcode = EVAL_ASSETS;
uint8_t funcId = (uint8_t)'t';
tokenid = revuint256(tokenid);
switch ( assetFuncId )
{
case 't': case 'x': case 'o':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid);
//case 't': this cannot be here
case 'x': case 'o':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId);
break;
case 's': case 'b': case 'S': case 'B':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << price << origpubkey);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId << price << origpubkey);
break;
case 'E': case 'e':
assetid2 = revuint256(assetid2);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << assetid2 << price << origpubkey);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcId << tokenid << assetFuncId << assetid2 << price << origpubkey);
break;
default:
fprintf(stderr,"EncodeOpRet: illegal funcid.%02x\n",funcid);
fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId);
opret << OP_RETURN;
break;
}
return(opret);
}
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
// it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet)
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description)
{
std::vector<uint8_t> vopret; uint8_t evalcode,funcid,*script;
GetOpReturnData(scriptPubKey, vopret);
@ -274,59 +281,87 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &or
return(0);
}
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey)
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey)
{
std::vector<uint8_t> vopret; uint8_t funcid=0,*script,e,f;
GetOpReturnData(scriptPubKey, vopret);
std::vector<uint8_t> vopret;
uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyFuncId;
uint256 dummyTokenid;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
memset(&assetid,0,sizeof(assetid));
memset(&assetid2,0,sizeof(assetid2));
price = 0;
if ( script != 0 && script[0] == EVAL_ASSETS )
{
funcid = script[1];
//fprintf(stderr,"decode.[%c]\n",funcid);
switch ( funcid )
if (script == 0) {
std::cerr << "DecodeAssetOpRet() script is empty" << std::endl;
return (uint8_t)0;
}
tokenid = zeroid;
assetid2 = zeroid;
price = 0;
bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof());
if (!result && isEof) { // NOTE: 'result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error'
std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << std::endl;
return (uint8_t)0;
}
tokenid = revuint256(tokenid);
std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl;
if(evalCodeInOpret == EVAL_ASSETS)
{
//fprintf(stderr,"decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId);
switch( assetFuncId )
{
case 'c': return(funcid);
break;
case 't': case 'x': case 'o':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid) != 0 )
/*case 'c':
return(funcid);
break; */
/*case 't':
if (E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> tokenid; isEof = ss.eof()) || !isEof)
{
assetid = revuint256(assetid);
return(funcid);
}
break; */
case 'x': case 'o':
if (isEof) // no data after 'assetFuncId' allowed
{
assetid = revuint256(assetid);
return(funcid);
return(assetFuncId);
}
break;
case 's': case 'b': case 'S': case 'B':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> price; ss >> origpubkey) != 0 )
if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> dummyTokenid; ss >> dummyFuncId; ss >> price; ss >> origpubkey) != 0)
{
assetid = revuint256(assetid);
//fprintf(stderr,"got price %llu\n",(long long)price);
return(funcid);
return(assetFuncId);
}
break;
case 'E': case 'e':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
if ( E_UNMARSHAL(vopret,ss >> dummyEvalCode; ss >> dummyFuncId; ss >> dummyTokenid; ss >> dummyFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
{
//fprintf(stderr,"got price %llu\n",(long long)price);
assetid = revuint256(assetid);
assetid2 = revuint256(assetid2);
return(funcid);
return(assetFuncId);
}
break;
default:
fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n",funcid);
funcid = 0;
fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n", funcId);
funcId = 0;
break;
}
}
return(funcid);
return(funcId);
}
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx)
{
uint256 assetid,assetid2;
if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 )
uint8_t evalCode;
if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode,assetid,assetid2,price,origpubkey) != 0 )
return(true);
else return(false);
}
@ -334,8 +369,10 @@ bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CT
bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,const CTransaction& tx)
{
uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector<uint8_t> origpubkey; CScript script;
uint8_t evalCode;
n = tx.vout.size();
if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey, evalCode,assetid,assetid2,price,origpubkey)) == 0 )
return(false);
if ( GetCCaddress(cp,CCaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 )
return(true);
@ -343,76 +380,6 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,co
}
// Checks if the vout is a really Asset CC vout
// if maxAssetExactAmountDepth > 0, it also validates the vin transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid)
{
uint256 assetid,assetid2; int64_t nValue=0; int32_t n; uint8_t funcid;
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) // maybe check address too?
{
if (maxAssetExactAmountDepth > 0) {
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
std::vector<CTransaction> ccVinsTxs;
//std::cerr << "IsAssetvout() validate=yes" << std::endl;
const bool validateVinTxs = false;
bool isEqualAmounts = AssetExactAmounts(maxAssetExactAmountDepth, cp, myCCVinsAmount, 0, myCCVoutsAmount, eval, tx, refassetid);
// if ccInputs != ccOutputs and it is not the tokenbase tx means it is possibly fake tx (dimxy):
if (!isEqualAmounts && refassetid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr << "IsAssetvout() detected bad tx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx" << std::endl;
return 0;
}
}
n = tx.vout.size();
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
nValue = tx.vout[v].nValue;
// fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f\n",v,n,(double)nValue/COIN);
if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
{
fprintf(stderr,"IsAssetvout() null decodeopret v.%d\n",v);
return(0);
}
else if ( funcid == 'c' )
{
if (refassetid == tx.GetHash() && v == 0) {
std::cerr << "isAssetVout() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning nValue=" << nValue << std::endl;
return(nValue);
}
}
else if ( (funcid == 'b' || funcid == 'B') && v == 0 ) // critical! 'b'/'B' vout0 is NOT asset
return(0);
else if ( funcid != 'E' )
{
if ( assetid == refassetid )
{
fprintf(stderr,"IsAssetvout() returning %.8f\n",(double)nValue/COIN);
return(nValue);
}
}
else if ( funcid == 'E' )
{
if ( v < 2 && assetid == refassetid )
return(nValue);
else if ( v == 2 && assetid2 == refassetid )
return(nValue);
}
}
//fprintf(stderr,"Isassetvout: normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{
uint256 hashBlock; char destaddr[64];
@ -446,16 +413,16 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch
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)
{
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid;
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode;
CCaddr[0] = origaddr[0] = 0;
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
return(0);
return(0);
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout0 for buyvin");
else
{
//fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr);
if ( vinTx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' )
if ( vinTx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey, evalCode, assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' )
return eval->Invalid("invalid opreturn for buyvin");
else if ( refassetid != assetid )
return eval->Invalid("invalid assetid for buyvin");
@ -469,88 +436,162 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
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)
{
CTransaction vinTx; int64_t nValue,assetoshis;
fprintf(stderr,"AssetValidateSellvin\n");
//fprintf(stderr,"AssetValidateSellvin\n");
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
return(0);
if ( (assetoshis= IsAssetvout(1, cp, NULL, tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 )
if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey,vinTx,0,assetid)) == 0 )
return eval->Invalid("invalid missing CC vout0 for sellvin");
else return(assetoshis);
}
// overload with additional params for deep tx validation (dimxy)
bool AssetExactAmounts(int maxDepth, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
// validates opret for asset tx:
bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector<uint8_t> &origpubkey) {
uint256 assetidOpret, assetidOpret2;
uint8_t funcid, evalCode;
// this is just for log messages indentation fur debugging recursive calls:
int32_t n = tx.vout.size();
if ((funcid = DecodeAssetOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0)
{
std::cerr << "ValidateAssetOpret() DecodeOpret returned null for n-1=" << n - 1 << " txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
/* it is now on token level:
else if (funcid == 'c')
{
if (assetid != zeroid && assetid == tx.GetHash() && v == 0) {
//std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
}
else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout
{
//std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (assetid != zeroid && assetid == assetidOpret) {
//std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
} */
// TODO: hope this was unneeded!!! (dimxy)
else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset
return(false);
else if (funcid != 'E')
{
if (assetid != zeroid && assetidOpret == assetid)
{
//std::cerr << "ValidateAssetOpret() returns true for not 'E', funcid=" << (char)funcid << std::endl;
return(true);
}
}
else if (funcid == 'E') // NOTE: not implemented yet!
{
if (v < 2 && assetid != zeroid && assetidOpret == assetid)
return(true);
else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid)
return(true);
}
//std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
return false;
}
// Checks if the vout is a really Asset CC vout
// compareTotals == true, the func also validates the passed transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid)
{
CTransaction vinTx; uint256 hashBlock,id,id2; int32_t i,flag,numvins,numvouts; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
numvins = tx.vin.size();
numvouts = tx.vout.size();
inputs = outputs = 0;
maxDepth--;
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
for (i=starti; i<numvins; i++)
{
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
{
//std::cerr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are really not inside validation! -- dimxy
if ( (eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) )
{
fprintf(stderr,"AssetExactAmounts() cannot read vintx i.%d starti.%d numvins.%d\n", i,starti,numvins);
return (!eval) ? false : eval->Invalid("always should find vin, but didnt");
} // false means 'don't go deeper' -- dimxy
else if ( (assetoshis= IsAssetvout( maxDepth, cp, eval, tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vin%d %llu, ",i,(long long)assetoshis);
inputs += assetoshis;
}
else
{
if ( vinTx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 && DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
{
assetoshis = vinTx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vin%d assetoshis=%llu special case, ",i,(long long)assetoshis);
inputs += assetoshis;
}
}
}
}
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_t n = tx.vout.size();
// just check boundaries:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
// moved opret checking to this new reusable func (dimxy):
const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey);
//std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
if (valOpret) {
//std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
flag = 1;
else flag = 0;
//fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
}
//fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
for (i=0; i<numvouts; i++)
{ // 'false' means 'dont go deep' -- dimxy
if ( (assetoshis= IsAssetvout(maxDepth, cp, eval, tmpprice,tmporigpubkey,tx,i,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu, ",i,(long long)assetoshis);
outputs += assetoshis;
}
// Note: account it only if this is 'transfer' tx -- dimxy
else if ( flag != 0 && tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
assetoshis = tx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu special case, ",i,(long long)assetoshis);
outputs += assetoshis;
}
}
// sets cc inputs vs cc outputs and ensures they are equal:
bool AssetExactAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
{
CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
struct CCcontract_info *cpTokens, C;
cpTokens = CCinit(&C, EVAL_TOKENS);
for (int32_t i = 0; i<numvins; i++)
{ // check for additional contracts which may send tokens to the Assets contract
if ((*cpAssets->ismyvin)(tx.vin[i].scriptSig) || (*cpTokens->ismyvin)(tx.vin[i].scriptSig)) // || IsVinAllowed(tx.vin[i].scriptSig) != 0)
{
//std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
std::cerr << "AssetExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
// validate vouts of vintx
//std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid);
if (assetoshis != 0)
{
std::cerr << "AssetExactAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl;
inputs += assetoshis;
}
}
}
}
//std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
// we do not use this flag anymore
//if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
//flag = 1;
//else
//flag = 0;
if ( inputs != outputs )
{
std::cerr << "AssetExactAmounts() incorrect inputs=" << (double)inputs / COIN << " vs outputs=" << (double)outputs/COIN << " for txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
else return(true);
}
for (int32_t i = 0; i<numvouts; i++)
{
// Note: we pass in here 'false' because we don't need to call AssetExactAmounts() recursively from IsAssetvout
// indeed, in this case we'll be checking this tx again
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid);
// overload for existing calls of this function (dimxy)
/*bool AssetExactAmounts(struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) {
std::vector<CTransaction> ccVinsTxs;
if (assetoshis != 0)
{
std::cerr << "AssetExactAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl;
outputs += assetoshis;
}
}
//std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
return AssetExactAmounts(true, cp, inputs, starti, outputs, eval, tx, assetid);
}*/
/* if (inputs != outputs) {
if (tx.GetHash() != assetid) {
std::cerr << "AssetExactAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs");
}
} */
return(true);
}

435
src/cc/CCassetstx.cpp

@ -14,6 +14,8 @@
******************************************************************************/
#include "CCassets.h"
//#include "CCtokens.h"
int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs)
{
@ -21,25 +23,31 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
threshold = total/(maxinputs!=0?maxinputs:64);
threshold = total/(maxinputs!=0?maxinputs:64); // TODO: is maxinputs really not over 64, what if i want to calc total balance?
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
vout = (int32_t)it->first.index;
if ( it->second.satoshis < threshold )
if (it->second.satoshis < threshold)
continue;
for (j=0; j<mtx.vin.size(); j++)
if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n)
break;
if ( j != mtx.vin.size() )
if( j != mtx.vin.size() )
continue;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
if( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
Getscriptaddress(destaddr,vintx.vout[vout].scriptPubKey);
if ( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 )
if( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 )
continue;
fprintf(stderr,"AddAssetInputs() check destaddress=%s vout amount=%.8f\n",destaddr,(double)vintx.vout[vout].nValue/COIN);
if ( (nValue= IsAssetvout(1, cp, NULL, price,origpubkey,vintx,vout,assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
if( (nValue = IsAssetvout(cp, price, origpubkey, vintx, vout, assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
{
if ( total != 0 && maxinputs != 0 )
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
@ -57,12 +65,13 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK
return(totalinputs);
}
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
return(AddAssetInputs(cp,mtx,pk,tokenid,0,0));
cp = CCinit(&C,EVAL_TOKENS);
return(AddTokenCCInputs(cp,mtx,pk,tokenid,0,0));
}
UniValue AssetInfo(uint256 assetid)
@ -75,11 +84,11 @@ UniValue AssetInfo(uint256 assetid)
result.push_back(Pair("error","cant find assetid"));
return(result);
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
if ( vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
fprintf(stderr,"assetid isnt token creation txid\n");
result.push_back(Pair("result","error"));
result.push_back(Pair("error","assetid isnt assetcreation txid"));
result.push_back(Pair("error","assetid isnt token creation txid"));
}
result.push_back(Pair("result","success"));
result.push_back(Pair("tokenid",uint256_str(str,assetid)));
@ -92,15 +101,20 @@ UniValue AssetInfo(uint256 assetid)
UniValue AssetList()
{
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description; char str[65];
cp = CCinit(&C,EVAL_ASSETS);
UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
struct CCcontract_info *cp,C; uint256 txid,hashBlock;
CTransaction vintx; std::vector<uint8_t> origpubkey;
std::string name,description; char str[65];
cp = CCinit(&C,EVAL_TOKENS);
SetCCtxids(addressIndex,cp->normaladdr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
{
txid = it->first.txhash;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) != 0 )
if ( vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) != 0 )
{
result.push_back(uint256_str(str,txid));
}
@ -112,17 +126,33 @@ UniValue AssetList()
UniValue AssetOrders(uint256 refassetid)
{
static uint256 zero;
int64_t price; uint256 txid,hashBlock,assetid,assetid2; std::vector<uint8_t> origpubkey; CTransaction vintx; UniValue result(UniValue::VARR); std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; uint8_t funcid; char numstr[32],funcidstr[16],origaddr[64],assetidstr[65]; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
SetCCunspents(unspentOutputs,(char *)cp->unspendableCCaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsAssets;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it)
{
uint256 txid, hashBlock, assetid, assetid2;
int64_t price;
std::vector<uint8_t> origpubkey;
CTransaction vintx;
uint8_t funcid, evalCode;
char numstr[32], funcidstr[16], origaddr[64], assetidstr[65];
txid = it->first.txhash;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
//std::cerr << "addOrders txid" << txid.GetHex() << std::endl;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
if ( vintx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey)) != 0 )
funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey);
//std::cerr << "addOrders vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << std::endl;
if (vintx.vout.size() > 0 && (funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0)
{
if ( refassetid != zero && assetid != refassetid )
if (refassetid != zero && assetid != refassetid)
{
//int32_t z;
//for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&txid)[z]);
@ -131,11 +161,13 @@ UniValue AssetOrders(uint256 refassetid)
//fprintf(stderr," assetid\n");
//for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&refassetid)[z]);
//fprintf(stderr," refassetid\n");
continue;
return;
}
if ( vintx.vout[it->first.index].nValue == 0 )
continue;
if (vintx.vout[it->first.index].nValue == 0)
return;
UniValue item(UniValue::VOBJ);
funcidstr[0] = funcid;
funcidstr[1] = 0;
item.push_back(Pair("funcid", funcidstr));
@ -157,8 +189,8 @@ UniValue AssetOrders(uint256 refassetid)
}
if ( origpubkey.size() == 33 )
{
GetCCaddress(cp,origaddr,pubkey2pk(origpubkey));
item.push_back(Pair("origaddress",origaddr));
GetCCaddress(cp, origaddr, pubkey2pk(origpubkey)); // TODO: what is this? is it asset or token??
item.push_back(Pair("origaddress", origaddr));
}
if ( assetid != zeroid )
item.push_back(Pair("tokenid",uint256_str(assetidstr,assetid)));
@ -184,11 +216,27 @@ UniValue AssetOrders(uint256 refassetid)
//fprintf(stderr,"func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN);
}
}
}
};
SetCCunspents(unspentOutputsTokens, (char *)cpTokens->unspendableCCaddr);
SetCCunspents(unspentOutputsAssets, (char *)cpAssets->unspendableCCaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin();
itTokens != unspentOutputsTokens.end();
itTokens++)
addOrders(cpTokens, itTokens);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itAssets = unspentOutputsAssets.begin();
itAssets != unspentOutputsAssets.end();
itAssets++)
addOrders(cpAssets, itAssets);
return(result);
}
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
// not used (use TokenCreate instead)
/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C;
@ -213,9 +261,10 @@ std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description)));
}
return("");
}
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
} */
// not used (use TokenTransfer instead)
/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
@ -230,11 +279,11 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
{
/*n = outputs.size();
if ( n == amounts.size() )
{
for (i=0; i<n; i++)
total += amounts[i];*/
//n = outputs.size();
//if ( n == amounts.size() )
//{
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 )
{
@ -254,9 +303,10 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
return("");
}
} */
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
// deprecated
/* std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
@ -281,76 +331,106 @@ std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> dest
} else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN);
}
return("");
}
} */
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal)
// rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens
std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C; uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description;
if ( bidamount < 0 || pricetotal < 0 )
CPubKey mypk;
struct CCcontract_info *cpAssets, C;
uint256 hashBlock;
CTransaction vintx;
std::vector<uint8_t> origpubkey;
std::string name,description;
int64_t inputs;
std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl;
if (bidamount < 0 || pricetotal < 0)
{
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n",(long long)bidamount,(long long)pricetotal);
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal);
return("");
}
if ( GetTransaction(assetid,vintx,hashBlock,false) == 0 )
if (GetTransaction(assetid, vintx, hashBlock, false) == 0)
{
fprintf(stderr,"cant find assetid\n");
return("");
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0)
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here!
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,bidamount+txfee,64) > 0 )
if ((inputs = AddNormalinputs(mtx, mypk, bidamount+txfee, 64)) > 0)
{
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount,GetUnspendable(cp,0)));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('b',assetid,zeroid,pricetotal,Mypubkey())));
std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl;
if (inputs < bidamount+txfee) {
std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl;
CCerror = strprintf("insufficient coins to make buy offer");
return ("");
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, GetUnspendable(cpAssets,0)));
return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, EncodeAssetOpRet('b', assetid, zeroid, pricetotal, Mypubkey())));
}
CCerror = strprintf("no coins found to make buy offer");
return("");
}
// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal'
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
CPubKey mypk;
uint64_t mask;
int64_t inputs, CCchange;
CScript opret;
struct CCcontract_info *cp,C;
//std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl;
if ( askamount < 0 || pricetotal < 0 )
{
if (askamount < 0 || pricetotal < 0) {
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C, EVAL_TOKENS); // NOTE: tokens is here
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,askamount,60)) > 0 )
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0)
{
if (inputs < askamount) {
//askamount = inputs;
//was: askamount = inputs;
std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,askamount, GetUnspendable(cp,0)));
if (inputs > askamount)
CCchange = (inputs - askamount);
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk));
opret = EncodeAssetOpRet('s',assetid, zeroid, pricetotal, Mypubkey());
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
}
else {
fprintf(stderr, "need some assets to place ask\n");
fprintf(stderr, "need some tokens to place ask\n");
}
}
else { // dimxy added 'else', because it was misleading message before
@ -363,34 +443,48 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
//////////////////////////////////////////
fprintf(stderr,"asset swaps disabled\n");
return("");
//////////////////////////////////////////
if ( askamount < 0 || pricetotal < 0 )
{
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C, EVAL_ASSETS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,askamount,60)) > 0 )
if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0)
{
if ( inputs < askamount )
askamount = inputs;
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
if (inputs < askamount) {
//was: askamount = inputs;
std::cerr << "CreateSwap(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, GetUnspendable(cp, 0)));
if (inputs > askamount)
CCchange = (inputs - askamount);
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
if ( assetid2 == zeroid )
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk));
if (assetid2 == zeroid)
opret = EncodeAssetOpRet('s', assetid, zeroid, pricetotal, Mypubkey());
else
{
opret = EncodeAssetOpRet('e',assetid,assetid2,pricetotal,Mypubkey());
opret = EncodeAssetOpRet('e', assetid, assetid2, pricetotal, Mypubkey());
}
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
}
@ -405,146 +499,213 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a
return("");
}
// unlocks coins
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t bidamount; CPubKey mypk; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
CTransaction vintx;
uint64_t mask;
uint256 hashBlock;
int64_t bidamount;
CPubKey mypk;
struct CCcontract_info *cp,C;
cp = CCinit(&C, EVAL_ASSETS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
bidamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(bidtxid,0,CScript()));
mtx.vin.push_back(CTxIn(bidtxid, 0, CScript()));
mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('o',assetid,zeroid,0,Mypubkey())));
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeAssetOpRet('o', assetid, zeroid, 0, Mypubkey())));
}
}
return("");
}
//unlocks tokens
std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; CPubKey mypk; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; CPubKey mypk;
struct CCcontract_info *cp,C;
cp = CCinit(&C, EVAL_TOKENS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
askamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(asktxid,0,CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('x',assetid,zeroid,0,Mypubkey())));
mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk));
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeAssetOpRet('x', assetid, zeroid, 0, Mypubkey())));
}
}
return("");
}
//send tokens, receive coins:
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; int32_t bidvout=0; uint64_t mask; int64_t origprice,bidamount,paid_amount,remaining_required,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillamount < 0 )
CTransaction vintx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
int32_t bidvout=0;
uint64_t mask;
int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0;
struct CCcontract_info *cp,C;
if (fillamount < 0)
{
fprintf(stderr,"negative fillamount %lld\n",(long long)fillamount);
fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cp = CCinit(&C, EVAL_TOKENS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
bidamount = vintx.vout[bidvout].nValue;
SetAssetOrigpubkey(origpubkey,origprice,vintx);
mtx.vin.push_back(CTxIn(bidtxid,bidvout,CScript()));
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,fillamount,60)) > 0 )
SetAssetOrigpubkey(origpubkey, origprice, vintx);
mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript()));
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, fillamount, 60)) > 0)
{
if ( inputs < fillamount )
fillamount = inputs;
SetBidFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice);
if ( inputs > fillamount )
if (inputs < fillamount) {
std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl;
CCerror = strprintf("insufficient tokens to fill buy offer");
return ("");
}
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)));
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, bidamount - paid_amount, GetUnspendable(cp,0))); // tokens
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,fillamount,pubkey2pk(origpubkey)));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required);
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('B',assetid,zeroid,remaining_required,origpubkey)));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, fillamount, pubkey2pk(origpubkey))); // coins
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); // coins
fprintf(stderr,"remaining %llu -> origpubkey\n", (long long)remaining_required);
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee, EncodeAssetOpRet('B', assetid, zeroid, remaining_required, origpubkey)));
} else return("dont have any assets to fill bid\n");
}
}
return("no normal coins left");
}
// send coins, receive tokens
std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillunits)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx,filltx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; double dprice; uint64_t mask; int32_t askvout=0; int64_t received_assetoshis,total_nValue,orig_assetoshis,paid_nValue,remaining_nValue,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillunits < 0 )
CTransaction vintx,filltx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
double dprice;
uint64_t mask;
int32_t askvout=0;
int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
if (fillunits < 0)
{
CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits);
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
if ( assetid2 != zeroid )
if (assetid2 != zeroid)
{
CCerror = "asset swaps disabled";
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx,mypk,txfee,3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
orig_assetoshis = vintx.vout[askvout].nValue;
SetAssetOrigpubkey(origpubkey,total_nValue,vintx);
SetAssetOrigpubkey(origpubkey, total_nValue, vintx);
dprice = (double)total_nValue / orig_assetoshis;
paid_nValue = dprice * fillunits;
mtx.vin.push_back(CTxIn(asktxid,askvout,CScript()));
if ( assetid2 != zeroid )
inputs = AddAssetInputs(cp,mtx,mypk,assetid2,paid_nValue,60);
mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); // NOTE: this is the reference to tokens -> send cpTokens for signing into FinalizeCCTx!
if (assetid2 != zeroid)
inputs = AddAssetInputs(cpTokens, mtx, mypk, assetid2, paid_nValue, 60);
else
{
inputs = AddNormalinputs(mtx,mypk,paid_nValue,60);
inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60);
mask = ~((1LL << mtx.vin.size()) - 1);
}
if ( inputs > 0 )
if (inputs > 0)
{
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 )
if (inputs < paid_nValue) {
std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl;
CCerror = strprintf("insufficient coins to fill sell");
return ("");
}
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));
if ( assetid2 != zeroid )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,paid_nValue,origpubkey));
else mtx.vout.push_back(CTxOut(paid_nValue,CScript() << origpubkey << OP_CHECKSIG));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet(assetid2!=zeroid?'E':'S',assetid,assetid2,remaining_nValue,origpubkey)));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // coins
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); // tokens
if (assetid2 != zeroid)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); // tokens (not implemented correctly)
else
mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); // coins
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); // coins
return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee,EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid, assetid2, remaining_nValue, origpubkey)));
} else {
CCerror = strprintf("filltx not enough utxos");
fprintf(stderr,"%s\n", CCerror.c_str());

22
src/cc/CCcustom.cpp

@ -30,6 +30,7 @@
#include "CCMarmara.h"
#include "CCPayments.h"
#include "CCGateways.h"
#include "CCtokens.h"
/*
CCcustom has most of the functions that need to be extended to create a new CC contract.
@ -222,6 +223,18 @@ uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0
#undef FUNCNAME
#undef EVALCODE
// Tokens
#define FUNCNAME IsTokensInput
#define EVALCODE EVAL_TOKENS
const char *TokensCCaddr = "RAMvUfoyURBRxAdVeTMHxn3giJZCFWeha2";
const char *TokensNormaladdr = "RCNgAngYAdrfzujYyPgfbjCGNVQZzCgTad";
char TokensCChexstr[67] = { "03e6191c70c9c9a28f9fd87089b9488d0e6c02fb629df64979c9cdb6b2b4a68d95" };
uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf5, 0xb6, 0x26, 0xd5, 0xad, 0xa0, 0xf0, 0x0a, 0xdd, 0x7a, 0x72, 0x7d, 0x17, 0x35, 0xb5, 0xe3, 0x2c, 0x6c, 0xa9, 0xa2, 0x03, 0x16, 0x4b, 0xcf };
#include "CCcustom.inc"
#undef FUNCNAME
#undef EVALCODE
struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
{
cp->evalcode = evalcode;
@ -347,6 +360,15 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
cp->validate = GatewaysValidate;
cp->ismyvin = IsGatewaysInput;
break;
case EVAL_TOKENS:
strcpy(cp->unspendableCCaddr, TokensCCaddr);
strcpy(cp->normaladdr, TokensNormaladdr);
strcpy(cp->CChexstr, TokensCChexstr);
memcpy(cp->CCpriv, TokensCCpriv, 32);
cp->validate = TokensValidate;
cp->ismyvin = IsTokensInput;
break;
}
return(cp);
}

17
src/cc/CCinclude.h

@ -133,13 +133,22 @@ int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *
uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format);
uint256 OracleMerkle(int32_t height,uint256 reforacletxid,char *format,std::vector<struct oracle_merklepair>publishers);
uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk);
int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
//int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<uint8_t> &vopretExtra);
//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector <uint8_t>&data);
int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
// CCcustom
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);

487
src/cc/CCtokens.cpp

@ -0,0 +1,487 @@
/******************************************************************************
* Copyright © 2014-2018 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#include "CCtokens.h"
/* TODO: correct this:
-----------------------------
The SetTokenFillamounts() and ValidateTokenRemainder() 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 tokens 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 tokenoshis to original pubkey
//vout.3: CC output for tokenoshis change (if any)
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey]
ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
Yes, this is quite confusing...
In ValudateTokenRemainder the naming convention is nValue is the coin/token 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 tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
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.
------------------------------
*/
// NOTE: this inital tx won't be used by other contract
// for tokens to be used there should be at least one 't' tx with other contract's custom opret
CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
{
CScript opret; uint8_t evalcode = EVAL_TOKENS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
return(opret);
}
// this is for other contracts which use tokens and build customized extra payloads to token's opret:
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<uint8_t> payload)
{
CScript opret;
//uint8_t evalcode = EVAL_TOKENS;
tokenid = revuint256(tokenid);
//uint8_t tokenFuncId = (isTransferrable) ? (uint8_t)'t' : (uint8_t)'l';
opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << payload);
return(opret);
}
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
{
std::vector<uint8_t> vopret; uint8_t dummyEvalcode, funcid, *script;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' )
{
if ( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 )
return(funcid);
}
return (uint8_t)0;
}
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<uint8_t> &vopretExtra)
{
std::vector<uint8_t> vopret, extra, dummyPubkey;
uint8_t funcid=0, *script, e, dummyFuncId;
std::string dummyName; std::string dummyDescription;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
tokenid = zeroid;
if (script != 0 /*enable all evals: && script[0] == EVAL_TOKENS*/)
{
bool isEof = true;
evalCode = script[0];
funcid = script[1];
//fprintf(stderr,"decode.[%c]\n",funcid);
switch ( funcid )
{
case 'c':
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription);
//break;
case 't':
//not used yet: case 'l':
if (E_UNMARSHAL(vopret, ss >> e; ss >> dummyFuncId; ss >> tokenid; isEof = ss.eof(); vopretExtra = std::vector<uint8_t>(ss.begin(), ss.end())) || !isEof)
{
tokenid = revuint256(tokenid);
return(funcid);
}
std::cerr << "DecodeTokenOpRet() isEof=" << isEof << std::endl;
fprintf(stderr, "DecodeTokenOpRet() bad opret format\n"); // this may be just check, no error logging
return (uint8_t)0;
default:
fprintf(stderr, "DecodeTokenOpRet() illegal funcid.%02x\n", funcid);
return (uint8_t)0;
}
}
return (uint8_t)0;
}
// tx validation
bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; std::vector<uint8_t> origpubkey, tmporigpubkey, ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], CCaddr[64];
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, origpubkey)) == 0)
return eval->Invalid("TokenValidate: invalid opreturn payload");
fprintf(stderr, "TokensValidate (%c)\n", funcid);
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
return eval->Invalid("cant find token create txid");
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
return eval->Invalid("illegal token vin0"); // why? (dimxy)
else if (numvouts < 1)
return eval->Invalid("no vouts");
else if (funcid != 'c')
{
if (tokenid == zeroid)
return eval->Invalid("illegal tokenid");
else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) {
if (!eval->Valid())
return false; //TokenExactAmounts must call eval->Invalid()!
else
return eval->Invalid("tokens cc inputs != cc outputs");
}
}
// init for forwarding validation call
struct CCcontract_info *cpOther = NULL, C;
if (evalCodeInOpret != EVAL_TOKENS)
cpOther = CCinit(&C, evalCodeInOpret);
switch (funcid)
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance tokenoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
if (evalCodeInOpret != EVAL_TOKENS)
return eval->Invalid("unexpected TokenValidate for createtoken");
else
return true;
case 't': // transfer
//vin.0: normal input
//vin.1 .. vin.n-1: valid CC outputs
//vout.0 to n-2: tokenoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
if (inputs == 0)
return eval->Invalid("no token inputs for transfer");
fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts);
break; // breaking to other contract validation...
default:
fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid);
return eval->Invalid("unexpected token funcid");
}
// forward validation if evalcode in opret is not EVAL_TOKENS
if (cpOther)
return cpOther->validate(cpOther, eval, tx, nIn);
else
return eval->Invalid("unsupported evalcode in opret");
// what does this do?
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
}
// this is just for log messages indentation fur debugging recursive calls:
thread_local uint32_t tokenValIndentSize = 0;
// validates opret for token tx:
bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector<uint8_t> &vopretExtra) {
uint256 tokenidOpret, tokenidOpret2;
uint8_t funcid, evalCode;
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
int32_t n = tx.vout.size();
if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, tokenidOpret, vopretExtra)) == 0)
{
std::cerr << indentStr << "ValidateTokenOpret() DecodeOpret returned null for n-1=" << n - 1 << " txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
else if (funcid == 'c')
{
if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) {
//std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
}
else if (funcid == 't')
{
//std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (tokenid != zeroid && tokenid == tokenidOpret) {
//std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
}
//std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
return false;
}
// Checks if the vout is a really Tokens CC vout
// compareTotals == true, the func also validates the passed transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, std::vector<uint8_t> &vopretExtra, const CTransaction& tx, int32_t v, uint256 reftokenid)
{
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
//std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl;
//TODO: validate cc vouts are EVAL_TOKENS!
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_t n = tx.vout.size();
// just check boundaries:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
if (compareTotals) {
//std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl;
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
tokenValIndentSize++;
// false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels
bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid);
tokenValIndentSize--;
if (!isEqual) {
// if ccInputs != ccOutputs and it is not the tokenbase tx
// this means it is possibly a fake tx (dimxy):
if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl;
return 0;
}
}
}
// moved opret checking to this new reusable func (dimxy):
const bool valOpret = ValidateTokenOpret(tx, v, reftokenid, vopretExtra);
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (valOpret) {
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid)
{
CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t tokenoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
// this is just for log messages indentation for debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
for (int32_t i = 0; i<numvins; i++)
{ // check for additional contracts which may send tokens to the Tokens contract
if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/)
{
//std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
tokenValIndentSize++;
// validate vouts of vintx
//std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
tokenoshis = IsTokensvout(compareTotals, cpTokens, eval, tmporigpubkey, vinTx, tx.vin[i].prevout.n, tokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl;
inputs += tokenoshis;
}
}
}
}
for (int32_t i = 0; i<numvouts; i++)
{
tokenValIndentSize++;
// Note: we pass in here 'false' because we don't need to call TokenExactAmounts() recursively from IsTokenvout
// indeed, in this case we'll be checking this tx again
tokenoshis = IsTokensvout(false, cpTokens, eval, tmporigpubkey, tx, i, tokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
std::cerr << indentStr << "TokensExactAmounts() vout i=" << i << " tokenoshis=" << tokenoshis << std::endl;
outputs += tokenoshis;
}
}
//std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
if (inputs != outputs) {
if (tx.GetHash() != tokenid)
std::cerr << indentStr << "TokenExactAmounts() found unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not create tx" << std::endl;
return false; // do not call eval->Invalid() here!
}
else
return true;
}
// add inputs from token cc addr
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs)
{
char coinaddr[64], destaddr[64];
int64_t threshold, nValue, price, totalinputs = 0;
uint256 txid, hashBlock;
std::vector<uint8_t> vopretExtra;
CTransaction vintx;
int32_t j, vout, n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp, coinaddr, pk);
SetCCunspents(unspentOutputs, coinaddr);
threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: is maxinputs really could not be over 64? what if i want to calc total balance?
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
{
txid = it->first.txhash;
vout = (int32_t)it->first.index;
if (it->second.satoshis < threshold)
continue;
for (j = 0; j<mtx.vin.size(); j++)
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n)
break;
if (j != mtx.vin.size())
continue;
if (GetTransaction(txid, vintx, hashBlock, false) != 0)
{
Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey);
if (strcmp(destaddr, coinaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0)
continue;
fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN);
if ((nValue = IsTokensvout(true, cp, NULL, vopretExtra, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0)
{
if (total != 0 && maxinputs != 0)
mtx.vin.push_back(CTxIn(txid, vout, CScript()));
nValue = it->second.satoshis;
totalinputs += nValue;
//std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl;
n++;
if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs))
break;
}
}
}
//std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl;
return(totalinputs);
}
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp, C;
if (assetsupply < 0)
{
fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply);
return("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (name.size() > 32 || description.size() > 4096)
{
fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size());
return("");
}
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0)
{
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk));
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG));
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description)));
}
return("");
}
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C;
std::vector<uint8_t> emptyExtraOpret;
if (total < 0)
{
fprintf(stderr, "negative total %lld\n", (long long)total);
return("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
//n = outputs.size();
//if ( n == amounts.size() )
//{
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, total, 60)) > 0)
{
if (inputs < total) { //added dimxy
std::cerr << "AssetTransfer(): insufficient funds" << std::endl;
return ("");
}
if (inputs > total)
CCchange = (inputs - total);
//for (i=0; i<n; i++)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, total, pubkey2pk(destpubkey)));
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk));
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet('t', EVAL_TOKENS, assetid, emptyExtraOpret))); // By setting EVA_TOKENS we're getting out from assets validation code
}
else fprintf(stderr, "not enough CC asset inputs for %.8f\n", (double)total / COIN);
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
return("");
}

76
src/cc/CCtokens.h

@ -0,0 +1,76 @@
/******************************************************************************
* Copyright © 2014-2018 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
/*
CCassetstx has the functions that create the EVAL_ASSETS transactions. It is expected that rpc calls would call these functions. For EVAL_ASSETS, the rpc functions are in rpcwallet.cpp
CCassetsCore has functions that are used in two contexts, both during rpc transaction create time and also during the blockchain validation. Using the identical functions is a good way to prevent them from being mismatched. The must match or the transaction will get rejected.
*/
#ifndef CC_TOKENS_H
#define CC_TOKENS_H
#include "CCinclude.h"
// CCcustom
bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
bool TokensExactAmounts(bool compareTotals, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid);
int64_t IsTokensvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid);
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description);
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total);
//this is in CCinclude.h int64_t AddTokenInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
/*
// CCassetsCore
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx);
int64_t IsAssetvout(bool compareTotals, struct CCcontract_info *cp, Eval* eval, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid);
bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool SetBidFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
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);
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);
bool AssetExactAmounts(bool compareTotals, struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid);
// CCassetstx
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid);
int64_t AddAssetInputs(CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
UniValue AssetOrders(uint256 tokenid);
UniValue AssetInfo(uint256 tokenid);
UniValue AssetList();
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal);
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid);
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount);
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal);
std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal);
std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid);
std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillamount);
*/
#endif

13
src/cc/CCtx.cpp

@ -144,9 +144,9 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
{
//fprintf(stderr,"matched %s unspendable2!\n",cp->unspendableaddr2);
privkey = cp->unspendablepriv2;
if ( othercond2 == 0 && cp->evalcode != EVAL_CHANNELS && cp->evalcode != EVAL_HEIR )
if ( othercond2 == 0 && cp->evalcode != EVAL_CHANNELS && cp->evalcode != EVAL_HEIR && cp->evalcode != EVAL_ASSETS && cp->evalcode != EVAL_TOKENS)
othercond2 = MakeCCcond1(cp->evalcode2,cp->unspendablepk2);
else if ( othercond2 == 0 && (cp->evalcode == EVAL_CHANNELS || cp->evalcode == EVAL_HEIR) )
else if ( othercond2 == 0 && (cp->evalcode == EVAL_CHANNELS || cp->evalcode == EVAL_HEIR || cp->evalcode == EVAL_ASSETS || cp->evalcode == EVAL_TOKENS) )
othercond2 = MakeCCcond1of2(cp->evalcode2,cp->unspendablepk2,cp->unspendablepk3);
cond = othercond2;
}
@ -297,7 +297,7 @@ int64_t CCfullsupply(uint256 tokenid)
uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector<uint8_t> origpubkey; std::string name,description;
if ( GetTransaction(tokenid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
if ( DecodeAssetCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description) > 0 )
if (DecodeTokenCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description))
{
return(tx.vout[0].nValue);
}
@ -307,8 +307,11 @@ int64_t CCfullsupply(uint256 tokenid)
int64_t CCtoken_balance(char *coinaddr,uint256 tokenid)
{
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock; std::vector<uint8_t> origpubkey;
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock;
std::vector<uint8_t> vopretExtra;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
uint8_t evalCode;
SetCCunspents(unspentOutputs,coinaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
@ -316,7 +319,7 @@ int64_t CCtoken_balance(char *coinaddr,uint256 tokenid)
if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
char str[65]; fprintf(stderr,"check %s %.8f\n",uint256_str(str,txid),(double)it->second.satoshis/COIN);
if ( DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 && assetid == tokenid )
if ( DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, assetid, vopretExtra) != 0 && assetid == tokenid )
{
sum += it->second.satoshis;
}

167
src/cc/assets.cpp

@ -133,45 +133,63 @@
// tx validation
bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey; uint8_t funcid; char destaddr[64],origaddr[64],CCaddr[64];
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2;
int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts;
int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64],origaddr[64],CCaddr[64];
// we need this for validating tokens' vins/vous:
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ( (funcid= DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,remaining_price,origpubkey)) == 0 )
return eval->Invalid("Invalid opreturn payload");
if((funcid = DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
return eval->Invalid("AssetValidate: invalid opreturn payload");
fprintf(stderr,"AssetValidate (%c)\n",funcid);
if ( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset create txid");
else if ( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset2 create txid");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
else if( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("illegal asset vin0");
else if ( numvouts < 1 )
else if( numvouts < 1 )
return eval->Invalid("no vouts");
else if ( funcid != 'c' )
else if( funcid != 'c' )
{
if ( funcid == 't' )
/* if( funcid == 't' )
starti = 0;
else starti = 1;
if ( assetid == zero )
else
starti = 1; */
if( assetid == zero )
return eval->Invalid("illegal assetid");
else if ( AssetExactAmounts(2, cp,inputs,starti,outputs,eval,tx,assetid) == false )
return eval->Invalid("asset inputs != outputs");
}
else if (!AssetExactAmounts(cpAssets, inputs, outputs, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
return false; // returns false if some problems with reading vintxes
}
}
switch ( funcid )
switch( funcid )
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance assetoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
return eval->Invalid("unexpected AssetValidate for createasset");
//if (evalCodeInOpret == EVAL_ASSETS)
// return eval->Invalid("unexpected AssetValidate for createasset");
// return
return eval->Invalid("invalid asset funcid \'c\'");
break;
case 't': // transfer
//vin.0: normal input
@ -179,9 +197,10 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0 to n-2: assetoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
if ( inputs == 0 )
return eval->Invalid("no asset inputs for transfer");
fprintf(stderr,"transfer validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
//if (inputs == 0)
// return eval->Invalid("no asset inputs for transfer");
//fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
return eval->Invalid("invalid asset funcid \'t\'");
break;
case 'b': // buyoffer
@ -189,13 +208,13 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0: amount of bid to unspendable
//vout.1: normal output for change (if any)
// vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
if ( remaining_price == 0 )
if( remaining_price == 0 )
return eval->Invalid("illegal null amount for buyoffer");
else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
else if( ConstrainVout(tx.vout[0],1,cpAssets->unspendableCCaddr,0) == 0 )
return eval->Invalid("invalid vout for buyoffer");
preventCCvins = 1;
preventCCvouts = 1;
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cp->unspendableCCaddr);
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr);
break;
case 'o': // cancelbuy
@ -204,9 +223,9 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['o']
if ( (nValue= AssetValidateBuyvin(cp,eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 )
return eval->Invalid("invalid refund for cancelbuy");
preventCCvins = 2;
preventCCvouts = 0;
@ -224,32 +243,32 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//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(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillbuy");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillbuy");
else
{
if ( nValue != tx.vout[0].nValue+tx.vout[1].nValue )
if( nValue != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillbuy");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
if( ConstrainVout(tx.vout[2], 1, CCaddr, 0) == 0 )
return eval->Invalid("vout2 doesnt go to origpubkey fillbuy");
else if ( inputs != tx.vout[2].nValue+tx.vout[3].nValue )
else if ( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy");
}
else if ( ConstrainVout(tx.vout[2],1,CCaddr,inputs) == 0 )
else if( ConstrainVout(tx.vout[2], 1, CCaddr, inputs) == 0 )
return eval->Invalid("vout2 doesnt match inputs fillbuy");
else if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
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,tx.vout[2].nValue,totalunits) == false )
else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillbuy");
else if ( remaining_price != 0 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
}
}
@ -266,16 +285,17 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
preventCCvouts = 1;
if ( remaining_price == 0 )
if( remaining_price == 0 )
return eval->Invalid("illegal null remaining_price for selloffer");
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
if( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
preventCCvouts++;
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
else if ( tx.vout[0].nValue+tx.vout[1].nValue != inputs )
else if( tx.vout[0].nValue + tx.vout[1].nValue != inputs )
return eval->Invalid("mismatched vout0+vout1 total for selloffer");
} else if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,inputs) == 0 )
}
else if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, inputs) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
break;
@ -286,9 +306,9 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
if ( (assetoshis= AssetValidateSellvin(cp,eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (assetoshis= AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
else if( ConstrainVout(tx.vout[0], 1, CCaddr, assetoshis) == 0 )
return eval->Invalid("invalid vout for cancel");
preventCCvins = 2;
preventCCvouts = 1;
@ -303,25 +323,25 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//'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 )
if( (assetoshis = AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillask");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillask");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillask");
if ( ValidateAskRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillask");
else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
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 )
else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 )
return eval->Invalid("normal vout1 for fillask");
else if ( remaining_price != 0 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
if ( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr,0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
}
}
@ -339,51 +359,58 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.3: CC output for asset2 change (if any)
//vout.3/4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
if ( AssetExactAmounts(1, 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 )
//if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false )
// eval->Invalid("asset2 inputs != outputs");
if( (assetoshis= AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, CCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillex");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillex");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillex");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
if( ConstrainVout(tx.vout[2], 1, CCaddr, 0) == 0 )
return eval->Invalid("vout2 doesnt go to origpubkey fillex");
else if ( inputs != tx.vout[2].nValue+tx.vout[3].nValue )
else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
{
fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
return eval->Invalid("asset inputs doesnt match vout2+3 fillex");
}
}
else if ( ConstrainVout(tx.vout[2],1,CCaddr,inputs) == 0 )
else if( ConstrainVout(tx.vout[2], 1, CCaddr, inputs) == 0 )
return eval->Invalid("vout2 doesnt match inputs fillex");
else if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
return eval->Invalid("vout1 is CC for fillex");
fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> 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[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillex");
else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
return eval->Invalid("normal vout1 for fillex");
else if ( remaining_price != 0 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex");
}
}
fprintf(stderr,"fill validated\n");
break;
default:
fprintf(stderr,"illegal assets funcid.(%c)\n",funcid);
return eval->Invalid("unexpected assets funcid");
break;
//break;
}
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
// what does this do?
bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts);
std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl;
return (bPrevent);
}

3
src/cc/eval.h

@ -55,7 +55,8 @@
EVAL(EVAL_PEGS, 0xee) \
EVAL(EVAL_MARMARA, 0xef) \
EVAL(EVAL_PAYMENTS, 0xf0) \
EVAL(EVAL_GATEWAYS, 0xf1)
EVAL(EVAL_GATEWAYS, 0xf1) \
EVAL(EVAL_TOKENS, 0xf2)
typedef uint8_t EvalCode;

1430
src/cc/heir.cpp

File diff suppressed because it is too large

622
src/cc/heir_validate.h

@ -0,0 +1,622 @@
#ifndef HEIR_VALIDATE_H
#define HEIR_VALIDATE_H
#include "CCinclude.h"
#include "CCHeir.h"
#define NOT_HEIR (-1)
#define HEIR_COINS 1
#define HEIR_TOKENS 2
#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos)
// makes coin initial tx opret
CScript EncodeHeirCreateOpRet(uint8_t eval, uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName);
// makes coin additional tx opret
CScript EncodeHeirOpRet(uint8_t eval, uint8_t funcid, uint256 fundingtxid);
CScript EncodeHeirAssetsCreateOpRet(uint8_t eval, uint8_t funcid, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string hearName);
CScript EncodeHeirAssetsOpRet(uint8_t eval, uint8_t funcid, uint256 tokenid, uint256 fundingtxid);
//CScript EncodeHeirConvertedAssetsOpRet(uint8_t eval, uint8_t funcid, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 fundingtxid);
template <class Helper> uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, bool &isHeirSpendingBegan);
template <class Helper> uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, uint256& fundingtxid, bool noLogging = false);
//template <class Helper> uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
template <class Helper> uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
//int64_t AddHeirTokenInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 reftokenid, int64_t total, int32_t maxinputs);
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins
class CoinHelper {
public:
static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
static uint8_t getMyEval() { return EVAL_HEIR; }
static int64_t addOwnerInputs(struct CCcontract_info* cp, uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
return AddNormalinputs(mtx, ownerPubkey, total, maxinputs);
}
static CScript makeCreateOpRet(uint256 dummyid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) {
return EncodeHeirCreateOpRet((uint8_t)EVAL_HEIR, (uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName);
}
static CScript makeAddOpRet(uint256 dummyid, uint256 fundingtxid) {
return EncodeHeirOpRet((uint8_t)EVAL_HEIR, (uint8_t)'A', fundingtxid);
}
static CScript makeClaimOpRet(uint256 dummyid, uint256 fundingtxid) {
return EncodeHeirOpRet((uint8_t)EVAL_HEIR, (uint8_t)'C', fundingtxid);
}
static void UnmarshalOpret(std::vector<uint8_t> vopret, uint8_t &e, uint8_t &funcId, uint256 &dummytokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret) {
E_UNMARSHAL(vopret, { ss >> e; ss >> funcId; ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; if (IS_CHARINSTR(funcId, "F")) { ss >> heirName; } if (IS_CHARINSTR(funcId, "AC")) { ss >> fundingTxidInOpret; } });
}
static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); }
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
}
static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
}
};
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens
class TokenHelper {
public:
static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
static uint8_t getMyEval() { return EVAL_TOKENS; }
static int64_t addOwnerInputs(struct CCcontract_info* cp, uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
return AddTokenCCInputs(cp, mtx, ownerPubkey, tokenid, total, maxinputs);
}
static CScript makeCreateOpRet(uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) {
return EncodeHeirAssetsCreateOpRet((uint8_t)EVAL_HEIR, (uint8_t)'F', tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName);
}
static CScript makeAddOpRet(uint256 tokenid, uint256 fundingtxid) {
return EncodeHeirAssetsOpRet((uint8_t)EVAL_HEIR, (uint8_t)'A', tokenid, fundingtxid);
}
static CScript makeClaimOpRet(uint256 tokenid, uint256 fundingtxid) {
return EncodeHeirAssetsOpRet((uint8_t)EVAL_HEIR, (uint8_t)'C', tokenid, fundingtxid);
}
static void UnmarshalOpret(std::vector<uint8_t> vopret, uint8_t &e, uint8_t &funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingtxidInOpret) {
uint8_t assetFuncId = '\0';
bool result = E_UNMARSHAL(vopret, { ss >> e; ss >> assetFuncId; ss >> tokenid; ss >> funcId; if (IS_CHARINSTR(funcId, "F")) { ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; } if (IS_CHARINSTR(funcId, "AC")) { ss >> fundingtxidInOpret; } });
if (!result /*|| assetFuncId != 't' -- any tx is ok*/)
funcId = 0;
}
static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); }
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey);
}
static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey);
}
};
//#define OPTIONAL_VOUT 0 // if vout is optional then in a validation plan it will be skipped without error, if all validators return false
/**
* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern:
* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators
* (These validators are derived from the class CValidatorBase).
*
* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn.
* Or, vin validator may check if this vin depicts correctly to the CC contract's address.
*
* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class.
* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it.
* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false.
*
* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied
* (see constructors of both CInputValidator and COutputValidator)
*
*
* Base class for all validators
*/
/**
* base class for all validators
*/
class CValidatorBase
{
public:
CValidatorBase(CCcontract_info* cp) : m_cp(cp) {}
virtual bool isVinValidator() const = 0;
virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0;
virtual bool validateVout(CTxOut vout, std::string& message) const = 0;
protected:
CCcontract_info * m_cp;
};
/**
* Base class for classes which identify vins as normal or cc inputs
*/
class CInputIdentifierBase
{
public:
CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {}
virtual std::string inputName() const = 0;
virtual bool identifyInput(CTxIn vin) const = 0;
protected:
CCcontract_info * m_cp;
};
/**
* Encapsulates an array containing rows of validators
* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts
* this validation plan is used for validating tx inputs
*/
template <typename TValidatorBase>
class CInputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
public:
// Pushes a row of validators for validating a vin or vout
// @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs")
// @param pargs parameter pack of zero or more pointer to validator objects
// Why pointers? because we store the base class in validators' row and then call its virtual functions
template <typename TValidatorBaseX, typename... ARGS>
void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
{
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
m_arrayValidators.push_back(std::make_pair(identifier, vValidators));
}
// validate tx inputs and corresponding prev tx vouts
bool validate(const CTransaction& tx, Eval* eval)
{
std::string message = "<empty>";
//std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl;
int32_t ival = 0;
int32_t iv = 0;
int32_t numv = tx.vin.size();
int32_t numValidators = m_arrayValidators.size();
// run over vins:
while (iv < numv && ival < numValidators) {
int32_t identifiedCount = 0;
CInputIdentifierBase *identifier = m_arrayValidators[ival].first;
// check if this is 'our' input:
while (iv < numv && identifier->identifyInput(tx.vin[iv])) {
// get prev tx:
CTransaction prevTx, *pPrevTxOrNull = NULL;
uint256 hashBlock;
if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) {
std::ostringstream stream;
stream << "can't find vinTx for vin=" << iv << ".";
return eval->Invalid(stream.str().c_str());
}
pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present)
// exec 'validators' from validator row of ival index, for tx.vin[iv]
if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) {
std::ostringstream stream;
stream << "invalid tx vin[" << iv << "]:" << message;
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
}
identifiedCount++; // how many vins we identified
iv++; // advance to the next vin
}
// CInputValidationPlan treats that there must be at least one identified vin for configured validators' row
// like in 'vin.0: normal input'
if (identifiedCount == 0) {
std::ostringstream stream;
stream << "can't find required vins for " << identifier->inputName() << ".";
return eval->Invalid(stream.str().c_str());
}
ival++; // advance to the next validator row
// and it will try the same vin with the new CInputIdentifierBase and validators row
}
// validation is successful if all validators have been used (i.e. ival = numValidators)
if (ival < numValidators) {
std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl;
return eval->Invalid("incorrect tx structure: not all required vins are present.");
}
std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl;
return true;
}
private:
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const
{
// check boundaries:
if (ival < 0 || ival >= m_arrayValidators.size()) {
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param ival index";
return false;
}
if (iv < 0 || iv >= pTx->vin.size()) {
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param iv index";
return false;
}
// get requested row of validators:
ValidatorsRow vValidators = m_arrayValidators[ival].second;
std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl;
for (auto v : vValidators) {
bool result;
if (v->isVinValidator())
// validate this vin and previous vout:
result = v->validateVin(pTx->vin[iv], pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage);
else
// if it is vout validator pass the previous tx vout:
result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage);
if (!result) {
return result;
}
}
return true; // validation OK
}
private:
//std::map<CInputIdentifierBase*, ValidatorsRow> m_arrayValidators;
std::vector< std::pair<CInputIdentifierBase*, ValidatorsRow> > m_arrayValidators;
};
/**
* Encapsulates an array containing rows of validators
* Each row is a vector of validators (zero is possible) for validating vouts
* this validation plan is used for validating tx outputs
*/
template <typename TValidatorBase>
class COutputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
public:
// Pushes a row of validators for validating a vout
// @param ivout index to vout to validate
// @param pargs parameter pack of zero or more pointer to validator objects
// Why pointers? because we store base class and call its virtual functions
template <typename TValidatorBaseX, typename... ARGS>
void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
{
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
m_arrayValidators.push_back(std::make_pair(ivout, vValidators));
}
// validate tx outputs
bool validate(const CTransaction& tx, Eval* eval)
{
std::string message = "<empty>";
//std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl;
int32_t ival = 0;
int32_t numVouts = tx.vout.size();
int32_t numValidators = m_arrayValidators.size();
// run over vouts:
while (ival < numValidators) {
int32_t ivout = m_arrayValidators[ival].first;
if (ivout >= numVouts) {
std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl;
return eval->Invalid("incorrect tx structure: not all required vouts are present.");
}
else
{
// exec 'validators' from validator row of ival index, for tx.vout[ivout]
if (!execValidatorsInRow(&tx, ivout, ival, message)) {
std::ostringstream stream;
stream << "invalid tx vout[" << ivout << "]:" << message;
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
}
}
ival++; // advance to the next vout
}
std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl;
return true;
}
private:
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const
{
// check boundaries:
if (ival < 0 || ival >= m_arrayValidators.size()) {
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param ival index";
return false;
}
if (iv < 0 || iv >= pTx->vout.size()) {
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param iv index";
return false;
}
// get requested row of validators:
ValidatorsRow vValidators = m_arrayValidators[ival].second;
std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl;
for (auto v : vValidators) {
if (!v->isVinValidator()) {
// if this is a 'in' validation plan then pass the previous tx vout:
bool result = v->validateVout(pTx->vout[iv], refMessage);
if (!result)
return result;
}
}
return true; // validation OK
}
private:
//std::map<int32_t, ValidatorsRow> m_mapValidators;
std::vector< std::pair<int32_t, ValidatorsRow> > m_arrayValidators;
};
class CNormalInputIdentifier : CInputIdentifierBase {
public:
CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
virtual std::string inputName() const { return std::string("normal input"); }
virtual bool identifyInput(CTxIn vin) const {
return !IsCCInput(vin.scriptSig);
}
};
class CCCInputIdentifier : CInputIdentifierBase {
public:
CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
virtual std::string inputName() const { return std::string("CC input"); }
virtual bool identifyInput(CTxIn vin) const {
return IsCCInput(vin.scriptSig);
}
};
/**
* Validates 1of2address for vout (may be used for either this or prev tx)
*/
template <class Helper> class CCC1of2AddressValidator : CValidatorBase
{
public:
CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") :
m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, std::string& message) const
{
//std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl;
uint8_t funcId;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
std::string heirName;
uint256 tokenid;
if ((funcId = DecodeHeirOpRet<Helper>(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName)) == 0) {
message = m_customMessage + std::string(" invalid opreturn format");
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
return false;
}
char shouldBeAddr[65], ccAddr[65];
GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey);
if (vout.scriptPubKey.IsPayToCryptoCondition()) {
if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) {
std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl;
return true;
}
else {
message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)");
}
}
else {
message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr");
}
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
return false;
}
virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return false; }
private:
CScript m_fundingOpretScript;
std::string m_customMessage;
};
/**
* Validates if this is vout to owner or heir from opret (funding or change)
*/
template <class Helper> class CMyPubkeyVoutValidator : CValidatorBase
{
public:
CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals)
: m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { }
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, std::string& message) const
{
//std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl;
uint8_t funcId;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
std::string heirName;
uint256 tokenid;
///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl;
// get both pubkeys:
if ((funcId = DecodeHeirOpRet<Helper>(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName)) == 0) {
message = std::string("invalid opreturn format");
return false;
}
CScript ownerScript;
CScript heirScript;
if (m_checkNormals) {
ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
}
else {
ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
}
//std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << std::endl;
// recreate scriptPubKey for owner and heir and compare it with that of the vout to check:
if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) {
// this is vout to owner or heir addr:
std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl;
return true;
}
std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl;
return false;
}
virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
//uint256 m_lasttxid;
bool m_checkNormals;
};
/**
* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime)
*/
template <class Helper> class CHeirSpendValidator : CValidatorBase
{
public:
CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, bool isHeirSpendingBegan)
: m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, std::string& message) const
{
//std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl;
uint8_t funcId;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
std::string heirName;
uint256 tokenid;
// get heir pubkey:
if ((funcId = DecodeHeirOpRet<Helper>(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, false)) == 0) {
message = std::string("invalid opreturn format");
return false;
}
int32_t numblocks;
int64_t durationSec = CCduration(numblocks, m_latesttxid);
// recreate scriptPubKey for heir and compare it with that of the vout:
if (vout.scriptPubKey == Helper::makeClaimerVout(vout.nValue, heirPubkey).scriptPubKey) {
// this is the heir is trying to spend
if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) {
message = "heir is not allowed yet to spend funds";
std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl;
return false;
}
else {
// heir is allowed to spend
return true;
}
}
std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl;
// this is not heir:
return true;
}
virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
uint256 m_latesttxid;
bool m_isHeirSpendingBegan;
};
/**
* Validates this opreturn and compares it with the opreturn from the previous tx
*/
template <class Helper> class COpRetValidator : CValidatorBase
{
public:
COpRetValidator(CCcontract_info* cp, CScript opret)
: m_fundingOpretScript(opret), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, std::string& message) const
{
//std::cerr << "COpRetValidator::validateVout() entered" << std::endl;
uint8_t funcId, initialFuncId; // do not check heir name
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid, initialTokenid;
if ((funcId = DecodeHeirOpRet<Helper>(vout.scriptPubKey, tokenid, fundingTxidInOpret)) == 0) {
message = std::string("invalid opreturn format");
return false;
}
if ((initialFuncId = DecodeHeirOpRet<Helper>(m_fundingOpretScript, initialTokenid, dummyTxid)) == 0) {
message = std::string("invalid initial tx opreturn format");
return false;
}
// validation rules:
if (!Helper::isMyFuncId(funcId)) {
message = std::string("invalid funcid in opret");
return false;
}
if(tokenid != initialTokenid ) {
message = std::string("invalid tokenid in opret");
return false;
}
std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl;
return true;
}
virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
};
#endif

15
src/rpc/server.cpp

@ -406,8 +406,16 @@ static const CRPCCommand vRPCCommands[] =
{ "faucet", "faucetget", &faucetget, true },
{ "faucet", "faucetaddress", &faucetaddress, true },
// Heir
{ "heir", "heiraddress", &heiraddress, true },
// Heir
{ "heir", "heiraddress", &heiraddress, true },
{ "heir", "heirfund", &heirfund, true },
{ "heir", "heiradd", &heiradd, true },
{ "heir", "heirclaim", &heirclaim, true },
{ "heir", "heirfundtokens", &heirfundtokens, true },
{ "heir", "heiraddtokens", &heiraddtokens, true },
{ "heir", "heirclaimtokens", &heirclaimtokens, true },
{ "heir", "heirinfo", &heirinfo, true },
{ "heir", "heirlist", &heirlist, true },
// Channels
{ "channels", "channelsaddress", &channelsaddress, true },
@ -479,7 +487,8 @@ static const CRPCCommand vRPCCommands[] =
{ "dice", "dicestatus", &dicestatus, true },
{ "dice", "diceaddress", &diceaddress, true },
// tokens
// tokens & assets
{ "tokens", "assetsaddress", &assetsaddress, true },
{ "tokens", "tokeninfo", &tokeninfo, true },
{ "tokens", "tokenlist", &tokenlist, true },
{ "tokens", "tokenorders", &tokenorders, true },

9
src/rpc/server.h

@ -242,6 +242,7 @@ extern UniValue tokeninfo(const UniValue& params, bool fHelp);
extern UniValue tokenlist(const UniValue& params, bool fHelp);
extern UniValue tokenorders(const UniValue& params, bool fHelp);
extern UniValue tokenbalance(const UniValue& params, bool fHelp);
extern UniValue assetsaddress(const UniValue& params, bool fHelp);
extern UniValue tokenaddress(const UniValue& params, bool fHelp);
extern UniValue tokencreate(const UniValue& params, bool fHelp);
extern UniValue tokentransfer(const UniValue& params, bool fHelp);
@ -253,6 +254,14 @@ extern UniValue tokencancelask(const UniValue& params, bool fHelp);
extern UniValue tokenfillask(const UniValue& params, bool fHelp);
extern UniValue tokenconvert(const UniValue& params, bool fHelp);
extern UniValue heiraddress(const UniValue& params, bool fHelp);
extern UniValue heirfund(const UniValue& params, bool fHelp);
extern UniValue heiradd(const UniValue& params, bool fHelp);
extern UniValue heirclaim(const UniValue& params, bool fHelp);
extern UniValue heirfundtokens(const UniValue& params, bool fHelp);
extern UniValue heiraddtokens(const UniValue& params, bool fHelp);
extern UniValue heirclaimtokens(const UniValue& params, bool fHelp);
extern UniValue heirinfo(const UniValue& params, bool fHelp);
extern UniValue heirlist(const UniValue& params, bool fHelp);
extern UniValue channelsaddress(const UniValue& params, bool fHelp);
extern UniValue oraclesaddress(const UniValue& params, bool fHelp);
extern UniValue oracleslist(const UniValue& params, bool fHelp);

441
src/wallet/rpcwallet.cpp

@ -5417,27 +5417,145 @@ UniValue gatewaysaddress(const UniValue& params, bool fHelp)
UniValue heiraddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> destPubkey;
/*
struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey;
cp = CCinit(&C,EVAL_HEIR);
if ( fHelp || params.size() > 1 )
throw runtime_error("heiraddress [pubkey]\n");
if ( ensure_CCrequirements() < 0 )
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
if ( params.size() == 1 )
pubkey = ParseHex(params[0].get_str().c_str());
return(CCaddress(cp,(char *)"Heir",pubkey));
*/
// make fake token tx:
struct CCcontract_info *cp, C;
if (fHelp || (params.size() < 1))
throw runtime_error("heiraddress A|G|H|T|R assetid|destpubkey amountcoins [heirpubkey] [fundingtxid]\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
char badKind = ((char *)params[0].get_str().c_str())[0];
if (badKind == 'A') {
std::vector<unsigned char> destPubkey;
if (params.size() == 2) {
cp = CCinit(&C, EVAL_HEIR);
destPubkey = ParseHex(params[1].get_str().c_str());
return(CCaddress(cp, (char *)"Heir", destPubkey));
}
else
return std::string("bad params for A");
}
cp = CCinit(&C,EVAL_HEIR);
if ( fHelp || (params.size() != 4 && params.size() != 3))
throw runtime_error("heiraddress func txid amount [destpubkey]\n");
if ( ensure_CCrequirements() < 0 )
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
//if ( params.size() == 1 )
// pubkey = ParseHex(params[0].get_str().c_str());
CPubKey myPubkey = pubkey2pk(Mypubkey());
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());;
if (badKind != 'R') {
if (badKind == 'G' && params.size() != 3)
return std::string("incorrect params for G");
if (badKind == 'H' && params.size() != 5)
return std::string("incorrect params for H, = 5");
if (badKind == 'T' && params.size() != 3)
return std::string("incorrect params for T, = 3");
uint256 assetid = Parseuint256((char *)params[1].get_str().c_str());
int64_t amount = atof(params[2].get_str().c_str()) * COIN;
uint256 fundingtxid;
CPubKey heirPubkey;
if (badKind == 'H') {
std::vector<unsigned char> heirPubkeyStr = ParseHex(params[3].get_str().c_str());
heirPubkey = pubkey2pk(heirPubkeyStr);
fundingtxid = Parseuint256((char *)params[4].get_str().c_str());
}
int64_t txfee = 10000;
uint8_t evalCodeInOpret = (badKind == 'H') ? EVAL_HEIR : EVAL_GATEWAYS;
cp = CCinit(&C, EVAL_ASSETS);
char funcid = ((char *)params[0].get_str().c_str())[0];
uint256 assetid = Parseuint256((char *)params[1].get_str().c_str());
int64_t funds = atof(params[2].get_str().c_str()) * COIN ;
if(params.size() == 4)
destPubkey = ParseHex(params[3].get_str().c_str());
int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60);
// int64_t ccInputs = 0;
//return HeirFundBad(funcid, assetid, funds, destPubkey);
return(CCaddress(cp,(char *)"Heir",destPubkey));
if (badKind == 'T') {
// just empty fake token
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, amount, myPubkey)); //note you need destPubkey for sending to gateways
}
else if (badKind == 'H') {
// heir add funding tx
mtx.vout.push_back(MakeCC1of2vout(EVAL_ASSETS, amount, myPubkey, heirPubkey)); // add cryptocondition to spend amount for either pk
}
else { // if (badKind == 'G')
CPubKey gatewayContractPubKey = GetUnspendable(cp, 0);
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(gatewayContractPubKey)) << OP_CHECKSIG));
}
int64_t change = (normalInputs - amount);
if (change != 0) {
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG));
}
std::cerr << "make fake token for contract=" << badKind << " added normalInputs=" << normalInputs << " change=" << change << std::endl;
// note: it sets eval=EVAL_ASSETS in opreturn both for tokens and gateways cc addr
//script = EncodeAssetOpRet('t', assetid, zeroid, 0, (badKind == 'A' ? Mypubkey() : destPubkey)); // dimxy: are we sure about destPubkey here? it may be just pubkey of the author...
CScript opret;
assetid = revuint256(assetid);
fundingtxid = revuint256(fundingtxid);
if (badKind == 'T')
opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)evalCodeInOpret << (uint8_t)'t' << assetid );
else if (badKind == 'H')
opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)evalCodeInOpret << (uint8_t)'t' << assetid << (uint8_t)'A' << myPubkey << heirPubkey << (int64_t)120 << fundingtxid);
else
opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)evalCodeInOpret << (uint8_t)'t' << assetid); // EVAL_GATEWAYS
return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); // normal change added here
}
else
{
// move vout from user cc addr to gateways unspendable
CTransaction srctx;
uint256 hashBlock;
const bool allowSlow = false;
uint256 srctxid = Parseuint256((char *)params[1].get_str().c_str());
struct CCcontract_info *cp, C;
cp = CCinit(&C, EVAL_GATEWAYS);
uint64_t txfee = 10000;
CPubKey gatewayspk = GetUnspendable(cp, 0);
if (myGetTransaction(srctxid, srctx, hashBlock) && srctx.vout.size() > 0) {
int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 2 * txfee, 4);
mtx.vin.push_back(CTxIn(srctxid, 2, CScript())); // repaired src tx
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS, srctx.vout[2].nValue, gatewayspk));
CScript script = srctx.vout[srctx.vout.size() - 1].scriptPubKey;
//mtx.fOverwintered = true;
return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, script)); // normal change added here
}
}
return std::string("there has been some error");
}
UniValue lottoaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey;
@ -5518,17 +5636,30 @@ UniValue rewardsaddress(const UniValue& params, bool fHelp)
return(CCaddress(cp,(char *)"Rewards",pubkey));
}
UniValue assetsaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp, C; std::vector<unsigned char> pubkey;
cp = CCinit(&C, EVAL_ASSETS);
if (fHelp || params.size() > 1)
throw runtime_error("assetsaddress [pubkey]\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
if (params.size() == 1)
pubkey = ParseHex(params[0].get_str().c_str());
return(CCaddress(cp, (char *)"Assets", pubkey));
}
UniValue tokenaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey;
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C,EVAL_TOKENS);
if ( fHelp || params.size() > 1 )
throw runtime_error("tokenaddress [pubkey]\n");
if ( ensure_CCrequirements() < 0 )
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
if ( params.size() == 1 )
pubkey = ParseHex(params[0].get_str().c_str());
return(CCaddress(cp,(char *)"Assets",pubkey));
return(CCaddress(cp,(char *)"Tokens", pubkey));
}
UniValue marmara_poolpayout(const UniValue& params, bool fHelp)
@ -6895,7 +7026,7 @@ UniValue tokencreate(const UniValue& params, bool fHelp)
return(result);
}
}
hex = CreateAsset(0,supply,name,description);
hex = CreateToken(0,supply,name,description);
if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
@ -6927,7 +7058,7 @@ UniValue tokentransfer(const UniValue& params, bool fHelp)
ERR_RESULT("amount must be positive");
return(result);
}
hex = AssetTransfer(0,tokenid,pubkey,amount);
hex = TokenTransfer(0,tokenid,pubkey,amount);
if (amount > 0) {
if ( hex.size() > 0 )
{
@ -6964,7 +7095,11 @@ UniValue tokenconvert(const UniValue& params, bool fHelp)
ERR_RESULT("amount must be positive");
return(result);
}
hex = AssetConvert(0,tokenid,pubkey,amount,evalcode);
ERR_RESULT("deprecated");
return(result);
/* hex = AssetConvert(0,tokenid,pubkey,amount,evalcode);
if (amount > 0) {
if ( hex.size() > 0 )
{
@ -6974,7 +7109,7 @@ UniValue tokenconvert(const UniValue& params, bool fHelp)
} else {
ERR_RESULT("amount must be positive");
}
return(result);
return(result); */
}
UniValue tokenbid(const UniValue& params, bool fHelp)
@ -7271,6 +7406,270 @@ UniValue getbalance64(const UniValue& params, bool fHelp)
return ret;
}
// heir contract functions for coins
UniValue heirfund(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ);
//uint256 txid;
int64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
//TODO: do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 5)
throw runtime_error("heirfund fee funds heirname heirpubkey inactivitytime\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atof((char*)params[0].get_str().c_str()) * COIN;
amount = atof((char*)params[1].get_str().c_str()) * COIN;
name = params[2].get_str();
pubkey = ParseHex(params[3].get_str().c_str());
inactivitytime = atof((char*)params[4].get_str().c_str());
hex = HeirFundCoinCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, zeroid);
if (hex.size() > 0) {
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
}
else
ERR_RESULT("couldn't create heir fund");
return result;
}
UniValue heiradd(const UniValue& params, bool fHelp)
{
UniValue result; // UniValue result(UniValue::VOBJ);
uint256 fundingtxid;
uint64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
// TODO: do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
throw runtime_error("heiradd fee funds fundingtxid\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atof((char*)params[0].get_str().c_str()) * COIN;
amount = atof((char*)params[1].get_str().c_str()) * COIN;
fundingtxid = Parseuint256((char*)params[2].get_str().c_str());
result = HeirAddCoinCaller(fundingtxid, txfee, amount);
/* if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
} else ERR_RESULT("couldn't claim heir fund"); */
return result;
}
UniValue heirclaim(const UniValue& params, bool fHelp)
{
UniValue result; // result(UniValue::VOBJ);
uint256 fundingtxid;
uint64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
// do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
throw runtime_error("heirclaim fee funds fundingtxid\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atof((char*)params[0].get_str().c_str()) * COIN;
amount = atof((char*)params[1].get_str().c_str()) * COIN;
fundingtxid = Parseuint256((char*)params[2].get_str().c_str());
result = HeirClaimCoinCaller(fundingtxid, txfee, amount);
/* if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
} else ERR_RESULT("couldn't claim heir fund"); */
return result;
}
// same heir contract functions for tokens
UniValue heirfundtokens(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ);
uint256 assetid;
uint64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
//TODO: do we need this (dimxy)?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 6)
throw runtime_error("heirfundtokens fee funds heirname heirpubkey inactivitytime assetid\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atoll((char*)params[0].get_str().c_str());
amount = atoll((char*)params[1].get_str().c_str());
name = params[2].get_str();
pubkey = ParseHex(params[3].get_str().c_str());
inactivitytime = atof((char*)params[4].get_str().c_str());
assetid = Parseuint256((char*)params[5].get_str().c_str());
hex = HeirFundTokenCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, assetid);
if (hex.size() > 0) {
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
}
else
ERR_RESULT("couldn't create heir fund");
return result;
}
UniValue heiraddtokens(const UniValue& params, bool fHelp)
{
UniValue result; // UniValue result(UniValue::VOBJ);
uint256 fundingtxid;
uint64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
// TODO: do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
throw runtime_error("heiraddtokens fee funds fundingtxid\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atoll((char*)params[0].get_str().c_str());
amount = atoll((char*)params[1].get_str().c_str());
fundingtxid = Parseuint256((char*)params[2].get_str().c_str());
result = HeirAddTokenCaller(fundingtxid, txfee, amount);
/* if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
} else ERR_RESULT("couldn't claim heir fund"); */
return result;
}
UniValue heirclaimtokens(const UniValue& params, bool fHelp)
{
UniValue result; // result(UniValue::VOBJ);
uint256 fundingtxid;
int64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
// do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
throw runtime_error("heirclaimtokens fee funds fundingtxid\n");
if (ensure_CCrequirements() < 0)
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
txfee = atoll((char*)params[0].get_str().c_str());
amount = atoll((char*)params[1].get_str().c_str());
fundingtxid = Parseuint256((char*)params[2].get_str().c_str());
result = HeirClaimTokenCaller(fundingtxid, txfee, amount);
/* if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
} else ERR_RESULT("couldn't claim heir fund"); */
return result;
}
UniValue heirinfo(const UniValue& params, bool fHelp)
{
uint256 fundingtxid;
if (fHelp || params.size() != 1) // or 0?
throw runtime_error("heirinfo fundingtxid\n");
// if ( ensure_CCrequirements() < 0 )
// throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
fundingtxid = Parseuint256((char*)params[0].get_str().c_str());
return (HeirInfo(fundingtxid));
}
UniValue heirlist(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0) // or 0?
throw runtime_error("heirlist\n");
// if ( ensure_CCrequirements() < 0 )
// throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
return (HeirList());
}
extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
extern UniValue importprivkey(const UniValue& params, bool fHelp);
extern UniValue importaddress(const UniValue& params, bool fHelp);

Loading…
Cancel
Save