diff --git a/src/Makefile.am b/src/Makefile.am index 49faa50c5..64103a3a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -257,6 +257,11 @@ libbitcoin_server_a_SOURCES = \ bloom.cpp \ cc/eval.cpp \ cc/import.cpp \ + cc/CCassetsCore.cpp \ + cc/CCassetstx.cpp \ + cc/CCcustom.cpp \ + cc/CCtx.cpp \ + cc/CCutils.cpp \ cc/assets.cpp \ cc/betprotocol.cpp \ chain.cpp \ diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h new file mode 100644 index 000000000..5ff35af8e --- /dev/null +++ b/src/cc/CCassets.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * 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 "CCinclude.h" + +/* + 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_ASSETS_H +#define CC_ASSETS_H + +// CCassetsCore +CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description); +CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,uint64_t price,std::vector origpubkey); +uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,uint64_t &price,std::vector &origpubkey); +bool SetAssetOrigpubkey(std::vector &origpubkey,uint64_t &price,CTransaction &tx); +uint64_t IsAssetvout(uint64_t &price,std::vector &origpubkey,CTransaction& tx,int32_t v,uint256 refassetid); +bool ValidateAssetRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received,uint64_t paid,uint64_t totalprice); +bool SetAssetFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice); +uint64_t AssetValidateBuyvin(Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,CTransaction &tx,uint256 refassetid); +uint64_t AssetValidateSellvin(Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,CTransaction &tx,uint256 assetid); +bool ConstrainAssetVout(CTxOut vout,int32_t CCflag,char *cmpaddr,uint64_t nValue); +bool AssetExactAmounts(Eval* eval,CTransaction &tx,uint256 assetid); + +// CCassetstx +uint64_t AddAssetInputs(CMutableTransaction &mtx,CPubKey pk,uint256 assetid,uint64_t total,int32_t maxinputs); +UniValue AssetOrders(uint256 tokenid); +std::string CreateAsset(uint64_t txfee,uint64_t assetsupply,std::string name,std::string description); +std::string AssetTransfer(uint64_t txfee,uint256 assetid,std::vector destpubkey,uint64_t total); +std::string CreateBuyOffer(uint64_t txfee,uint64_t bidamount,uint256 assetid,uint64_t pricetotal); +std::string CancelBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid); +std::string FillBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid,uint64_t fillamount); +std::string CreateSell(uint64_t txfee,uint64_t askamount,uint256 assetid,uint256 assetid2,uint64_t pricetotal); +std::string CancelSell(uint64_t txfee,uint256 assetid,uint256 asktxid); +std::string FillSell(uint64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,uint64_t fillamount); + +#endif diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp new file mode 100644 index 000000000..d0a4856a5 --- /dev/null +++ b/src/cc/CCassetsCore.cpp @@ -0,0 +1,297 @@ +/****************************************************************************** + * 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 "CCassets.h" + +CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector 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,uint64_t price,std::vector origpubkey) +{ + CScript opret; uint8_t evalcode = EVAL_ASSETS; + assetid = revuint256(assetid); + switch ( funcid ) + { + case 't': case 'x': case 'o': + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid); + break; + case 's': case 'b': case 'S': case 'B': + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << price << origpubkey); + break; + case 'E': case 'e': + assetid2 = revuint256(assetid2); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << assetid2 << price << origpubkey); + break; + default: + fprintf(stderr,"EncodeOpRet: illegal funcid.%02x\n",funcid); + opret << OP_RETURN; + break; + } + return(opret); +} + +uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,uint64_t &price,std::vector &origpubkey) +{ + std::vector vopret; uint8_t funcid=0,*script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + memset(&assetid,0,sizeof(assetid)); + memset(&assetid2,0,sizeof(assetid2)); + price = 0; + if ( script[0] == EVAL_ASSETS ) + { + funcid = script[1]; + //fprintf(stderr,"decode.[%c]\n",funcid); + switch ( funcid ) + { + case 'c': return(funcid); + break; + case 't': case 'x': case 'o': + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid) != 0 ) + { + assetid = revuint256(assetid); + return(funcid); + } + break; + case 's': case 'b': case 'S': case 'B': + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> price; ss >> origpubkey) != 0 ) + { + assetid = revuint256(assetid); + //fprintf(stderr,"got price %llu\n",(long long)price); + return(funcid); + } + break; + case 'E': case 'e': + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) + { + fprintf(stderr,"got price %llu\n",(long long)price); + assetid = revuint256(assetid); + assetid2 = revuint256(assetid2); + return(funcid); + } + break; + default: + fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n",funcid); + funcid = 0; + break; + } + } + return(funcid); +} + +bool SetAssetOrigpubkey(std::vector &origpubkey,uint64_t &price,CTransaction &tx) +{ + uint256 assetid,assetid2; + if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 ) + return(true); + else return(false); +} + +bool GetAssetorigaddrs(char *CCaddr,char *destaddr,CTransaction& tx) +{ + uint256 assetid,assetid2; uint64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector origpubkey; CScript script; + n = tx.vout.size(); + if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 ) + return(false); + if ( GetCCaddress(EVAL_ASSETS,destaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 ) + return(true); + else return(false); +} + +uint64_t IsAssetvout(uint64_t &price,std::vector &origpubkey,CTransaction& tx,int32_t v,uint256 refassetid) +{ + uint256 assetid,assetid2; uint64_t nValue=0; int32_t n; uint8_t funcid; + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + n = tx.vout.size(); + nValue = tx.vout[v].nValue; + //fprintf(stderr,"CC vout v.%d of n.%d %.8f\n",v,n,(double)nValue/COIN); + if ( v >= n-1 ) + return(0); + if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 ) + { + fprintf(stderr,"null decodeopret\n"); + return(0); + } + else if ( funcid == 'c' ) + { + if ( refassetid == tx.GetHash() && v == 0 ) + return(nValue); + } + else if ( funcid == 'b' || funcid == 'B' ) + return(0); + else if ( funcid != 'E' ) + { + if ( assetid == refassetid ) + 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); +} + +bool ValidateAssetRemainder(uint64_t remaining_price,uint64_t remaining_nValue,uint64_t orig_nValue,uint64_t received,uint64_t paid,uint64_t totalprice) +{ + uint64_t price,recvprice; + if ( orig_nValue == 0 || received == 0 || paid == 0 || totalprice == 0 ) + { + fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received == %llu || paid == %llu || totalprice == %llu\n",(long long)orig_nValue,(long long)received,(long long)paid,(long long)totalprice); + return(false); + } + else if ( remaining_price < (totalprice - received) ) + { + fprintf(stderr,"ValidateAssetRemainder: remaining_price %llu < %llu (totalprice %llu - %llu received)\n",(long long)remaining_price,(long long)(totalprice - received),(long long)totalprice,(long long)received); + return(false); + } + else if ( remaining_nValue < (orig_nValue - paid) ) + { + fprintf(stderr,"ValidateAssetRemainder: remaining_nValue %llu < %llu (totalprice %llu - %llu received)\n",(long long)remaining_nValue,(long long)(orig_nValue - paid),(long long)orig_nValue,(long long)paid); + return(false); + } + else if ( remaining_nValue > 0 ) + { + price = (totalprice * COIN) / orig_nValue; + recvprice = (received * COIN) / paid; + if ( recvprice < price ) + { + fprintf(stderr,"recvprice %llu < %llu price\n",(long long)recvprice,(long long)price); + return(false); + } + } + return(true); +} + +bool SetAssetFillamounts(uint64_t &paid,uint64_t &remaining_price,uint64_t orig_nValue,uint64_t &received,uint64_t totalprice) +{ + uint64_t remaining_nValue,price,mult; + if ( received >= totalprice ) + received = totalprice; + remaining_price = (totalprice - received); + price = (totalprice * COIN) / orig_nValue; + mult = (received * COIN); + fprintf(stderr,"remaining %llu price %llu, mult %llu, totalprice %llu, received %llu, paid %llu\n",(long long)remaining_price,(long long)price,(long long)mult,(long long)totalprice,(long long)received,(long long)mult / price); + if ( price > 0 && (paid= mult / price) > 0 ) + { + if ( (mult % price) != 0 ) + paid--; + remaining_nValue = (orig_nValue - paid); + return(ValidateAssetRemainder(remaining_price,remaining_nValue,orig_nValue,received,paid,totalprice)); + } else return(false); +} + +uint64_t AssetValidateCCvin(Eval* eval,char *CCaddr,char *origaddr,CTransaction &tx,CTransaction &vinTx) +{ + uint256 hashBlock; char destaddr[64]; + origaddr[0] = destaddr[0] = 0; + if ( tx.vin.size() < 2 ) + return eval->Invalid("not enough for CC vins"); + else if ( tx.vin[1].prevout.n != 0 ) + return eval->Invalid("vin1 needs to be buyvin.vout[0]"); + else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("always should find vin, but didnt"); + else if ( Getscriptaddress(destaddr,vinTx.vout[0].scriptPubKey) == 0 || strcmp(destaddr,(char *)AssetsCCaddr) != 0 ) + { + fprintf(stderr,"%s vs %s\n",destaddr,(char *)AssetsCCaddr); + return eval->Invalid("invalid vin AssetsCCaddr"); + } + else if ( vinTx.vout[0].nValue < 10000 ) + return eval->Invalid("invalid dust for buyvin"); + else if ( GetAssetorigaddrs(CCaddr,origaddr,vinTx) == 0 ) + return eval->Invalid("couldnt get origaddr for buyvin"); + fprintf(stderr,"Got %.8f to origaddr.(%s)\n",(double)vinTx.vout[0].nValue/COIN,origaddr); + return(vinTx.vout[0].nValue); +} + +uint64_t AssetValidateBuyvin(Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,CTransaction &tx,uint256 refassetid) +{ + CTransaction vinTx; uint64_t nValue; uint256 assetid,assetid2; uint8_t funcid; + if ( (nValue= AssetValidateCCvin(eval,CCaddr,origaddr,tx,vinTx)) == 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 ( (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' ) + return eval->Invalid("invalid opreturn for buyvin"); + else if ( refassetid != assetid ) + { + //for (i=32; i>=0; i--) + // fprintf(stderr,"%02x",((uint8_t *)&assetid)[i]); + //fprintf(stderr," AssetValidateBuyvin\n"); + return eval->Invalid("invalid assetid for buyvin"); + } + } + return(nValue); +} + +uint64_t AssetValidateSellvin(Eval* eval,uint64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,CTransaction &tx,uint256 assetid) +{ + CTransaction vinTx; uint64_t nValue,assetoshis; + fprintf(stderr,"AssetValidateSellvin\n"); + if ( (nValue= AssetValidateCCvin(eval,CCaddr,origaddr,tx,vinTx)) == 0 ) + return(0); + if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,0,assetid)) != 0 ) + return eval->Invalid("invalid missing CC vout0 for sellvin"); + else return(assetoshis); +} + +bool ConstrainAssetVout(CTxOut vout,int32_t CCflag,char *cmpaddr,uint64_t nValue) +{ + char destaddr[64]; + if ( vout.scriptPubKey.IsPayToCryptoCondition() != CCflag ) + return(false); + else if ( cmpaddr != 0 && (Getscriptaddress(destaddr,vout.scriptPubKey) == 0 || strcmp(destaddr,cmpaddr) != 0) ) + return(false); + else if ( (nValue == 0 && vout.nValue < 10000) || nValue != vout.nValue ) + return(false); + else return(true); +} + +bool AssetExactAmounts(Eval* eval,CTransaction &tx,uint256 assetid) +{ + CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t inputs=0,outputs=0,assetoshis; std::vector tmporigpubkey; uint64_t tmpprice; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + for (i=1; iGetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("always should find vin, but didnt"); + else if ( (assetoshis= IsAssetvout(tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 ) + inputs += assetoshis; + } + } + for (i=0; i origpubkey; CTransaction vintx; int32_t n = 0; + std::vector > unspentOutputs; + GetCCaddress(EVAL_ASSETS,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr); + //std::sort(unspentOutputs.begin(), unspentOutputs.end(), heightSort); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( (nValue= IsAssetvout(price,origpubkey,vintx,(int32_t)it->first.index,assetid)) > 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } + } + } + return(totalinputs); +} + +UniValue AssetOrders(uint256 refassetid) +{ + uint64_t price; uint256 txid,hashBlock,assetid,assetid2; std::vector origpubkey; CTransaction vintx; UniValue result(UniValue::VARR); std::vector > unspentOutputs; uint8_t funcid; char funcidstr[16],origaddr[64],assetidstr[65]; + SetCCunspents(unspentOutputs,(char *)AssetsCCaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( (funcid= DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey)) != 0 ) + { + UniValue item(UniValue::VOBJ); + funcidstr[0] = funcid; + funcidstr[1] = 0; + item.push_back(Pair("funcid", funcidstr)); + item.push_back(Pair("txid", uint256_str(assetidstr,txid))); + item.push_back(Pair("vout", (int64_t)it->first.index)); + item.push_back(Pair("amount", (double)vintx.vout[it->first.index].nValue/COIN)); + if ( funcid == 'b' || funcid == 'B' ) + item.push_back(Pair("bidamount",(double)vintx.vout[0].nValue/COIN)); + else item.push_back(Pair("askamount",(double)vintx.vout[0].nValue)); + if ( origpubkey.size() == 33 ) + { + GetCCaddress(EVAL_ASSETS,origaddr,pubkey2pk(origpubkey)); + item.push_back(Pair("origaddress",origaddr)); + } + if ( assetid != zeroid ) + item.push_back(Pair("tokenid",uint256_str(assetidstr,assetid))); + if ( assetid2 != zeroid ) + item.push_back(Pair("otherid",uint256_str(assetidstr,assetid2))); + if ( price > 0 ) + { + item.push_back(Pair("totalrequired", (int64_t)price)); + item.push_back(Pair("price", (double)vintx.vout[0].nValue / (price * COIN))); + } + result.push_back(item); + //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); + } + } + } + return(result); +} + +std::string CreateAsset(uint64_t txfee,uint64_t assetsupply,std::string name,std::string description) +{ + CMutableTransaction mtx; CPubKey mypk; + 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(0); + } + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,assetsupply+2*txfee,64) > 0 ) + { + mtx.vout.push_back(MakeAssetsVout(assetsupply,mypk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(AssetsCChexstr) << OP_CHECKSIG)); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description))); + } + return(0); +} + +std::string AssetTransfer(uint64_t txfee,uint256 assetid,std::vector destpubkey,uint64_t total) +{ + CMutableTransaction mtx; CPubKey mypk; uint64_t CCchange=0,inputs=0; //int32_t i,n; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + /*n = outputs.size(); + if ( n == amounts.size() ) + { + for (i=0; i 0 ) + { + if ( inputs > total ) + CCchange = (inputs - total); + //for (i=0; i 0 ) + { + mtx.vout.push_back(MakeAssetsVout(bidamount,GetUnspendable(EVAL_ASSETS,0))); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetOpRet('b',assetid,zeroid,pricetotal,Mypubkey()))); + } + return(0); +} + +std::string CreateSell(uint64_t txfee,uint64_t askamount,uint256 assetid,uint256 assetid2,uint64_t pricetotal) +{ + CMutableTransaction mtx; CPubKey mypk; uint64_t inputs,CCchange; CScript opret; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + if ( (inputs= AddAssetInputs(mtx,mypk,assetid,askamount,60)) > 0 ) + { + mtx.vout.push_back(MakeAssetsVout(askamount,GetUnspendable(EVAL_ASSETS,0))); + if ( inputs > askamount ) + CCchange = (inputs - askamount); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeAssetsVout(CCchange,mypk)); + if ( assetid2 == zeroid ) + opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey()); + else opret = EncodeAssetOpRet('e',assetid,assetid2,pricetotal,Mypubkey()); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,opret)); + } + } + return(0); +} + +std::string CancelBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid) +{ + CMutableTransaction mtx; CTransaction vintx; uint256 hashBlock; uint64_t bidamount; CPubKey mypk; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 ) + { + bidamount = vintx.vout[0].nValue; + mtx.vin.push_back(CTxIn(bidtxid,0,CScript())); + mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetOpRet('o',assetid,zeroid,0,Mypubkey()))); + } + } + return(0); +} + +std::string CancelSell(uint64_t txfee,uint256 assetid,uint256 asktxid) +{ + CMutableTransaction mtx; CTransaction vintx; uint256 hashBlock; uint64_t askamount; CPubKey mypk; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 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(MakeAssetsVout(askamount,mypk)); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetOpRet('x',assetid,zeroid,0,Mypubkey()))); + } + } + return(0); +} + +std::string FillBuyOffer(uint64_t txfee,uint256 assetid,uint256 bidtxid,uint64_t fillamount) +{ + CTransaction vintx; uint256 hashBlock; CMutableTransaction mtx; CPubKey mypk; std::vector origpubkey; int32_t bidvout=0; uint64_t origprice,bidamount,paid_amount,remaining_required,inputs,CCchange=0; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 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(mtx,mypk,assetid,fillamount,60)) > 0 ) + { + if ( inputs > fillamount ) + CCchange = (inputs - fillamount); + SetAssetFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice); + mtx.vout.push_back(MakeAssetsVout(bidamount - paid_amount,GetUnspendable(EVAL_ASSETS,0))); + mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeAssetsVout(fillamount,pubkey2pk(origpubkey))); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeAssetsVout(CCchange,mypk)); + fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetOpRet('B',assetid,zeroid,remaining_required,origpubkey))); + } else fprintf(stderr,"filltx wasnt for assetid\n"); + } + } + return(0); +} + +std::string FillSell(uint64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,uint64_t fillamount) +{ + CTransaction vintx,filltx; uint256 hashBlock; CMutableTransaction mtx; CPubKey mypk; std::vector origpubkey; int32_t askvout=0; uint64_t origprice,askamount,paid_amount,remaining_required,inputs,CCchange=0; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 ) + { + askamount = vintx.vout[askvout].nValue; + SetAssetOrigpubkey(origpubkey,origprice,vintx); + mtx.vin.push_back(CTxIn(asktxid,askvout,CScript())); + if ( assetid2 == zeroid ) + inputs = AddAssetInputs(mtx,mypk,assetid2,fillamount,60); + else inputs = AddNormalinputs(mtx,mypk,fillamount,60); + if ( inputs > 0 ) + { + if ( assetid2 == zeroid && inputs > fillamount ) + CCchange = (inputs - fillamount); + SetAssetFillamounts(paid_amount,remaining_required,askamount,fillamount,origprice); + mtx.vout.push_back(MakeAssetsVout(askamount - paid_amount,GetUnspendable(EVAL_ASSETS,0))); + mtx.vout.push_back(MakeAssetsVout(paid_amount,mypk)); + mtx.vout.push_back(MakeAssetsVout(fillamount,pubkey2pk(origpubkey))); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeAssetsVout(CCchange,mypk)); + fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required); + return(FinalizeCCTx(EVAL_ASSETS,mtx,mypk,txfee,EncodeAssetOpRet(assetid2==zeroid?'E':'S',assetid,assetid2,remaining_required,origpubkey))); + } else fprintf(stderr,"filltx not enough utxos\n"); + } + } + return(0); +} diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp new file mode 100644 index 000000000..551706759 --- /dev/null +++ b/src/cc/CCcustom.cpp @@ -0,0 +1,89 @@ +/****************************************************************************** + * 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 "CCinclude.h" + +/* + CCcustom has most of the functions that need to be extended to create a new CC contract. + + EVAL_CONTRACT is the naming convention and it should be added to cc/eval.h + + A CC scriptPubKey can only be spent if it is properly signed and validated. By constraining the vins and vouts, it is possible to implement a variety of functionality. CC vouts have an otherwise non-standard form, but it is properly supported by the enhanced bitcoin protocol code as a "cryptoconditions" output and the same pubkey will create a different address. + + This allows creation of a special address(es) for each contract type, which has the privkey public. That allows anybody to properly sign and spend it, but with the constraints on what is allowed in the validation code, the contract functionality can be implemented. + */ + +//BTCD Address: RAssetsAtGnvwgK9gVHBbAU4sVTah1hAm5 +//BTCD Privkey: UvtvQVgVScXEYm4J3r4nE4nbFuGXSVM5pKec8VWXwgG9dmpWBuDh +//BTCD Address: RSavingsEYcivt2DFsxsKeCjqArV6oVtVZ +//BTCD Privkey: Ux6XQekTxokko6gZHz24B7PUsmUQtWFzG2W9nUA8jba7UoVbPBF4 +const char *AssetsCCaddr = "RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6" ;//"RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u"; +char AssetsCChexstr[67] = { "02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702" }; +uint8_t AssetsCCpriv[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 }; + +CPubKey GetUnspendable(uint8_t evalcode,uint8_t *unspendablepriv) +{ + static CPubKey nullpk; + if ( unspendablepriv != 0 ) + memset(unspendablepriv,0,32); + if ( evalcode == EVAL_ASSETS ) + { + if ( unspendablepriv != 0 ) + memcpy(unspendablepriv,AssetsCCpriv,32); + } else return(nullpk); + return(pubkey2pk(ParseHex(AssetsCChexstr))); +} + +CC *MakeCC(uint8_t evalcode,CPubKey pk) +{ + if ( evalcode == EVAL_ASSETS ) + { + std::vector pks; + pks.push_back(CCNewSecp256k1(pk)); + CC *assetCC = CCNewEval(E_MARSHAL(ss << evalcode)); + CC *Sig = CCNewThreshold(1, pks); + return CCNewThreshold(2, {assetCC, Sig}); + } else return(0); +} + +bool GetCCaddress(uint8_t evalcode,char *destaddr,CPubKey pk) +{ + CC *payoutCond; + destaddr[0] = 0; + if ( evalcode == EVAL_ASSETS ) + { + if ( pk.size() == 0 ) + pk = GetUnspendable(EVAL_ASSETS,0); + if ( (payoutCond= MakeAssetCond(pk)) != 0 ) + { + Getscriptaddress(destaddr,CCPubKey(payoutCond)); + cc_free(payoutCond); + } + return(destaddr[0] != 0); + } + fprintf(stderr,"%02x is invalid evalcode\n",evalcode); + return false; +} + +bool ProcessCCevals(uint8_t evalcode,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) +{ + switch ( evalcode ) + { + case EVAL_ASSETS: + return ProcessAssets(this, vparams, txTo, nIn); + break; + } + return(false); +} diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h new file mode 100644 index 000000000..796d06d5a --- /dev/null +++ b/src/cc/CCinclude.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +#ifndef CC_INCLUDE_H +#define CC_INCLUDE_H + +#include +#include