Hush Full Node software. We were censored from Github, this is where all development happens now. https://hush.is
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

767 lines
34 KiB

// Copyright (c) 2016-2020 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
/******************************************************************************
* Copyright © 2014-2019 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 "cc/eval.h"
#include "cc/utils.h"
#include "importcoin.h"
#include "crosschain.h"
#include "primitives/transaction.h"
#include "cc/CCinclude.h"
#include <wolfssl/wolfcrypt/sha.h>
#include "cc/CCtokens.h"
#include "key_io.h"
#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA"
/*
* CC Eval method for import coin.
*
* This method should control every parameter of the ImportCoin transaction, since it has no signature
* to protect it from malleability.
##### 0xffffffff is a special CCid for single chain/dual daemon imports
*/
extern std::string ASSETCHAINS_SELFIMPORT;
extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT;
extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33];
extern uint256 HUSH_EARLYTXID;
// utilities from gateways.cpp
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids);
uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid);
int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid);
uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype);
int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vector<uint8_t>proof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2);
char *nonportable_path(char *str);
char *portable_path(char *str);
void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep);
void *filestr(long *allocsizep,char *_fname);
cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5)
{
char cmdstr[5000],fname[256],*jsonstr;
long fsize;
cJSON *retjson=NULL;
sprintf(fname,"/tmp/coda.%s",arg0);
sprintf(cmdstr,"coda.exe client %s %s %s %s %s %s > %s 2>&1",arg0,arg1,arg2,arg3,arg4,arg5,fname);
*retstr = 0;
if (system(cmdstr)<0) return (retjson);
if ( (jsonstr=(char *)filestr(&fsize,fname)) != 0 )
{
jsonstr[strlen(jsonstr)-1]='\0';
if ( (strncmp(jsonstr,"Merkle List of transactions:",28)!=0) || (retjson= cJSON_Parse(jsonstr+29)) == 0)
*retstr=jsonstr;
else free(jsonstr);
}
return(retjson);
}
// makes source tx for self import tx
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount)
{
const int64_t txfee = 10000;
int64_t inputs, change;
CPubKey myPubKey = Mypubkey();
struct CCcontract_info *cpDummy, C;
cpDummy = CCinit(&C, EVAL_TOKENS); // this is just for FinalizeCCTx to work
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
if (AddNormalinputs(mtx, myPubKey, 2 * txfee, 4) == 0) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl);
}
CScript scriptPubKey = GetScriptForDestination(dest);
mtx.vout.push_back(CTxOut(txfee, scriptPubKey));
//make opret with 'burned' amount:
FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
return mtx;
}
// make sure vin is signed by pubkey33
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33])
{
CTransaction vintx;
uint256 blockHash;
char destaddr[64], pkaddr[64];
if (i < 0 || i >= sourcetx.vin.size())
return false;
if( !myGetTransaction(sourcetx.vin[i].prevout.hash, vintx, blockHash) ) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl);
return false;
}
if( sourcetx.vin[i].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[i].prevout.n].scriptPubKey) != 0 )
{
pubkey2addr(pkaddr, pubkey33);
if (strcmp(pkaddr, destaddr) == 0) {
return true;
}
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
}
return false;
}
// ac_import=PUBKEY support:
// prepare a tx for creating import tx and quasi-burn tx
int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull) // find burnTx with hash from "other" daemon
{
MerkleBranch newBranch;
CMutableTransaction tmpmtx;
//CTransaction sourcetx;
tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
/*
if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl);
return(-1);
}
if (sourcetx.vout.size() == 0) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl);
return -1;
} */
/*if (ivout < 0) { // "ivout < 0" means "find"
// try to find vout
CPubKey myPubkey = Mypubkey();
ivout = 0;
// skip change:
if (sourcetx.vout[ivout].scriptPubKey == (CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG))
ivout++;
}
if (ivout >= sourcetx.vout.size()) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl);
return -1;
} */
int32_t ivout = 0;
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
CScript scriptPubKey = sourceMtx.vout[ivout].scriptPubKey;
//mtx is template for import tx
templateMtx = sourceMtx;
templateMtx.fOverwintered = tmpmtx.fOverwintered;
//malleability fix for burn tx:
//mtx.nExpiryHeight = tmpmtx.nExpiryHeight;
templateMtx.nExpiryHeight = sourceMtx.nExpiryHeight;
templateMtx.nVersionGroupId = tmpmtx.nVersionGroupId;
templateMtx.nVersion = tmpmtx.nVersion;
templateMtx.vout.clear();
templateMtx.vout.resize(1);
uint8_t evalCode, funcId;
int64_t burnAmount;
vscript_t vopret;
if( !GetOpReturnData(sourceMtx.vout.back().scriptPubKey, vopret) ||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> burnAmount)) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl);
return -1;
}
templateMtx.vout[0].nValue = burnAmount;
templateMtx.vout[0].scriptPubKey = scriptPubKey;
// not sure we need this now as we create sourcetx ourselves:
/*if (sourcetx.GetHash() != sourcetxid) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
return(-1);
}*/
// check ac_pubkey:
if (!CheckVinPubKey(sourceMtx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
return -1;
}
proofNull = ImportProof(std::make_pair(sourceMtx.GetHash(), newBranch));
return 0;
}
// make import tx with burntx and dual daemon
std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector<CTxOut> vouts)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()),burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
CPubKey mypk; uint256 codaburntxid; std::vector<unsigned char> dummyproof;
int32_t i,numvouts,n,m; std::string coin,error; struct CCcontract_info *cp, C;
cJSON *result,*tmp,*tmp1; unsigned char hash[SHA256_DIGEST_LENGTH+1];
char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; TxProof txProof; uint64_t amount;
cp = CCinit(&C, EVAL_GATEWAYS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, receipt.c_str(), receipt.size());
SHA256_Final(hash, &sha256);
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(out + (i * 2), "%02x", hash[i]);
}
out[65]='\0';
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: hash=" << out << std::endl);
codaburntxid.SetHex(out);
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receipt=" << receipt << " codaburntxid=" << codaburntxid.GetHex().data() << " amount=" << (double)amount / COIN << std::endl);
result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),"");
if (result==0)
{
if (retstr!=0)
{
CCerror=std::string("CodaRPC: ")+retstr;
free(retstr);
}
return("");
}
else
{
if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))!=0 && (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))!=0 &&
(receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))!=0 && (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))!=0)
{
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receiver=" << receiver << " destaddr=" << destaddr << " amount=" << amount << std::endl);
if (strcmp(receiver,CODA_BURN_ADDRESS)!=0)
{
CCerror="MakeCodaImportTx: invalid burn address, coins do not go to predefined burn address - ";
CCerror+=CODA_BURN_ADDRESS;
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
free(result);
return("");
}
CTxDestination dest = DecodeDestination(destaddr);
CScript scriptPubKey = GetScriptForDestination(dest);
if (vouts[0]!=CTxOut(amount*COIN,scriptPubKey))
{
CCerror="MakeCodaImportTx: invalid destination address, burnTx memo!=importTx destination";
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
free(result);
return("");
}
if (amount*COIN!=vouts[0].nValue)
{
CCerror="MakeCodaImportTx: invalid amount, burnTx amount!=importTx amount";
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
free(result);
return("");
}
burntx.vin.push_back(CTxIn(codaburntxid,0,CScript()));
burntx.vout.push_back(MakeBurnOutput(amount*COIN,0xffffffff,"CODA",vouts,dummyproof,srcaddr,receipt));
return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof,burntx,vouts)));
}
else
{
CCerror="MakeCodaImportTx: invalid Coda burn tx";
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
free(result);
return("");
}
}
CCerror="MakeCodaImportTx: error fetching Coda tx";
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
free(result);
return("");
}
// use proof from the above functions to validate the import
int32_t CheckBEAMimport(TxProof proof,std::vector<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
{
// check with dual-BEAM daemon via ASSETCHAINS_BEAMPORT for validity of burnTx
return(-1);
}
int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector<CTxOut> payouts,std::string srcaddr,std::string receipt)
{
cJSON *result,*tmp,*tmp1; char *retstr,out[SHA256_DIGEST_LENGTH*2+1]; unsigned char hash[SHA256_DIGEST_LENGTH+1]; int i,n,m;
SHA256_CTX sha256; uint256 codaburntxid; char *destaddr,*receiver; uint64_t amount;
// check with dual-CODA daemon via ASSETCHAINS_CODAPORT for validity of burnTx
SHA256_Init(&sha256);
SHA256_Update(&sha256, receipt.c_str(), receipt.size());
SHA256_Final(hash, &sha256);
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(out + (i * 2), "%02x", hash[i]);
}
out[65]='\0';
codaburntxid.SetHex(out);
result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),"");
if (result==0)
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CodaRPC error: " << retstr << std::endl);
free(retstr);
return (-1);
}
else
{
if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))==0 || (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))==0 ||
(receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))==0 || (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))==0)
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid Coda burn tx" << jprint(result,1) << std::endl);
free(result);
return (-1);
}
CTxDestination dest = DecodeDestination(destaddr);
CScript scriptPubKey = GetScriptForDestination(dest);
if (payouts[0]!=CTxOut(amount*COIN,scriptPubKey));
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Destination address in burn tx does not match destination in import tx" << std::endl);
free(result);
return (-1);
}
if (strcmp(receiver,CODA_BURN_ADDRESS)!=0)
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid burn address " << jstr(tmp1,(char *)"receiver") << std::endl);
free(result);
return (-1);
}
if (amount*COIN!=payouts[0].nValue)
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Burn amount and import amount not matching, " << j64bits(tmp,(char *)"amount") << " - " << payouts[0].nValue/COIN << std::endl);
free(result);
return (-1);
}
if (burnTx.vin[0].prevout.hash!=codaburntxid || importTx.vin[0].prevout.hash!=burnTx.GetHash())
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid import/burn tx vin" << std::endl);
free(result);
return (-1);
}
free(result);
}
return(0);
}
int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector<uint8_t> proof,
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount)
{
CTransaction oracletx,bindtx,regtx; int32_t i,m,n=0,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype;
uint256 txid,oracletxid,tmporacletxid,merkleroot,mhash,hashBlock;
std::string name,desc,format,coin; std::vector<CTxOut> vouts; CPubKey regpk;
std::vector<CPubKey> pubkeys,tmppubkeys,tmppublishers; char markeraddr[64],deposit[64],destaddr[64],tmpdest[64]; int64_t datafee;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
// ASSETCHAINS_SELFIMPORT is coin
if (HUSH_EARLYTXID!=zeroid && bindtxid!=HUSH_EARLYTXID)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is " << HUSH_EARLYTXID.GetHex() << std::endl);
return(-1);
}
// check for valid burn from external coin blockchain and if valid return(0);
if (myGetTransaction(bindtxid, bindtx, hashBlock) == 0 || (numvouts = bindtx.vout.size()) <= 0)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find bindtxid=" << bindtxid.GetHex() << std::endl);
return(-1);
}
else if (DecodeImportGatewayBindOpRet(deposit,bindtx.vout[numvouts - 1].scriptPubKey,coin,oracletxid,M,N,tmppubkeys,taddr,prefix,prefix2,wiftype) != 'B')
{
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid bind tx. bindtxid=" << bindtxid.GetHex() << std::endl);
return(-1);
}
else if (refcoin!=coin)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid coin " << refcoin << "!=" << coin << std::endl);
return(-1);
}
else if ( N == 0 || N > 15 || M > N )
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid N or M " << std::endl);
return(-1);
}
else if (tmppubkeys.size()!=N)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport not enough pubkeys for given N " << std::endl);
return(-1);
}
else if (komodo_txnotarizedconfirmed(bindtxid) == false)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport bindtx not yet confirmed/notarized" << std::endl);
return(-1);
}
else if (myGetTransaction(oracletxid, oracletx, hashBlock) == 0 || (numvouts = oracletx.vout.size()) <= 0)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl);
return(-1);
}
else if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C')
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl);
return(-1);
}
else if (name!=refcoin || format!="Ihh")
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl);
return(-1);
}
CCtxidaddr(markeraddr,oracletxid);
SetCCunspents(unspentOutputs,markeraddr,false);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
if ( myGetTransaction(txid,regtx,hashBlock) != 0 && regtx.vout.size() > 0
&& DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid )
{
pubkeys.push_back(regpk);
n++;
}
}
if (pubkeys.size()!=tmppubkeys.size())
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of bind and oracle pubkeys " << tmppubkeys.size() << "!=" << pubkeys.size() << std::endl);
return(-1);
}
merkleroot = zeroid;
for (i = m = 0; i < n; i++)
{
if ((mhash = CCOraclesReverseScan("importgateway-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid)
{
if (merkleroot == zeroid)
merkleroot = mhash, m = 1;
else if (mhash == merkleroot)
m ++;
tmppublishers.push_back(pubkeys[i]);
}
}
if (publishers.size()!=tmppublishers.size())
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of publishers for burtx in oracle" << std::endl);
return(-1);
}
else if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl );
return(-1);
}
else if ( ImportGatewayVerify(deposit,oracletxid,burnvout,refcoin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount )
{
CCerror = strprintf("burntxid didnt validate !");
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
return(-1);
}
else if (importTx.vout[0].nValue!=amount)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import amount different than in burntx" << std::endl);
return(-1);
}
Getscriptaddress(destaddr,importTx.vout[0].scriptPubKey);
Getscriptaddress(tmpdest,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
if (strcmp(destaddr,tmpdest)!=0)
{
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import coins destination different than in burntx" << std::endl);
return(-1);
}
return(0);
}
int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
{
// if burnTx has ASSETCHAINS_PUBKEY vin, it is valid return(0);
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "proof txid=" << proof.first.GetHex() << std::endl);
uint256 sourcetxid = proof.first, hashBlock;
CTransaction sourcetx;
if (!myGetTransaction(sourcetxid, sourcetx, hashBlock)) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "could not load source txid=" << sourcetxid.GetHex() << std::endl);
return -1;
}
if (sourcetx.vout.size() == 0) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "no vouts in source txid=" << sourcetxid.GetHex() << std::endl);
return -1;
}
// might be malleable:
if (burnTx.nExpiryHeight != sourcetx.nExpiryHeight) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "burntx nExpiryHeight incorrect for source txid=" << sourcetxid.GetHex() << std::endl);
return -1;
}
//ac_pubkey check:
if (!CheckVinPubKey(sourcetx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
return -1;
}
// get source tx opret:
std::vector<uint8_t> vopret;
uint8_t evalCode, funcId;
int64_t amount;
if (!GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret) ||
vopret.size() == 0 ||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) ||
evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
return -1;
}
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "importTx amount=" << payouts[0].nValue << " burnTx amount=" << burnTx.vout[0].nValue << " opret amount=" << amount << " source txid=" << sourcetxid.GetHex() << std::endl);
// amount malleability check with the opret from the source tx:
if (payouts[0].nValue != amount) { // assume that burntx amount is checked in the common code in Eval::ImportCoin()
LOGSTREAM("importcoin", CCLOG_INFO, stream << "importTx amount != amount in the opret of source txid=" << sourcetxid.GetHex() << std::endl);
return -1;
}
return(0);
}
bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction &burnTx, std::vector<CTxOut> & payouts, const ImportProof &proof, const std::vector<uint8_t> &rawproof)
{
vscript_t vimportOpret;
if (!GetOpReturnData(importTx.vout.back().scriptPubKey, vimportOpret) ||
vimportOpret.empty())
return eval->Invalid("invalid-import-tx-no-opret");
uint256 tokenid = zeroid;
if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens)
struct CCcontract_info *cpTokens, CCtokens_info;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
uint8_t evalCodeInOpret;
std::vector<CPubKey> voutTokenPubkeys;
vscript_t vnonfungibleOpret;
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
if (DecodeTokenOpRet(importTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
return eval->Invalid("cannot-decode-import-tx-token-opret");
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init to no non-fungibles
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
if (!vnonfungibleOpret.empty())
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
// check if burn tx at least has cc evaltoken vins (we cannot get cc input)
bool hasTokenVin = false;
for (auto vin : burnTx.vin)
if (cpTokens->ismyvin(vin.scriptSig))
hasTokenVin = true;
if (!hasTokenVin)
return eval->Invalid("burn-tx-has-no-token-vins");
// calc outputs for burn tx
CAmount ccBurnOutputs = 0;
for (auto v : burnTx.vout)
if (v.scriptPubKey.IsPayToCryptoCondition() &&
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
ccBurnOutputs += v.nValue;
// calc outputs for import tx
CAmount ccImportOutputs = 0;
for (auto v : importTx.vout)
if (v.scriptPubKey.IsPayToCryptoCondition() &&
!IsTokenMarkerVout(v)) // should not be marker here
ccImportOutputs += v.nValue;
if (ccBurnOutputs != ccImportOutputs)
return eval->Invalid("token-cc-burned-output-not-equal-cc-imported-output");
}
else if (vimportOpret.begin()[0] != EVAL_IMPORTCOIN) {
return eval->Invalid("import-tx-incorrect-opret-eval");
}
// for tokens check burn, import, tokenbase tx
if (!tokenid.IsNull()) {
std::string sourceSymbol;
CTransaction tokenbaseTx;
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbaseTx))
return eval->Invalid("cannot-unmarshal-rawproof-for-tokens");
uint256 sourceTokenId;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
uint8_t evalCodeInOpret;
std::vector<CPubKey> voutTokenPubkeys;
if (burnTx.vout.size() > 0 && DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, sourceTokenId, voutTokenPubkeys, oprets) == 0)
return eval->Invalid("cannot-decode-burn-tx-token-opret");
if (sourceTokenId != tokenbaseTx.GetHash()) // check tokenid in burn tx opret maches the passed tokenbase tx (to prevent cheating by importing user)
return eval->Invalid("incorrect-token-creation-tx-passed");
std::vector<std::pair<uint8_t, vscript_t>> opretsSrc;
vscript_t vorigpubkeySrc;
std::string nameSrc, descSrc;
if (DecodeTokenCreateOpRet(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0)
return eval->Invalid("cannot-decode-token-creation-tx");
std::vector<std::pair<uint8_t, vscript_t>> opretsImport;
vscript_t vorigpubkeyImport;
std::string nameImport, descImport;
if (importTx.vout.size() == 0 || DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0)
return eval->Invalid("cannot-decode-token-import-tx");
// check that name,pubkey,description in import tx correspond ones in token creation tx in the source chain:
if (vorigpubkeySrc != vorigpubkeyImport ||
nameSrc != nameImport ||
descSrc != descImport)
return eval->Invalid("import-tx-token-params-incorrect");
}
// Check burntx shows correct outputs hash
// if (payoutsHash != SerializeHash(payouts)) // done in ImportCoin
// return eval->Invalid("wrong-payouts");
TxProof merkleBranchProof;
std::vector<uint256> notaryTxids;
// Check proof confirms existance of burnTx
if (proof.IsMerkleBranch(merkleBranchProof)) {
uint256 target = merkleBranchProof.second.Exec(burnTx.GetHash());
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Eval::ImportCoin() momom target=" << target.GetHex() << " merkleBranchProof.first=" << merkleBranchProof.first.GetHex() << std::endl);
if (!CheckMoMoM(merkleBranchProof.first, target)) {
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl);
return eval->Invalid("momom-check-fail");
}
}
else if (proof.IsNotaryTxids(notaryTxids)) {
if (!CheckNotariesApproval(burnTx.GetHash(), notaryTxids)) {
return eval->Invalid("notaries-approval-check-fail");
}
}
else {
return eval->Invalid("invalid-import-proof");
}
/* if (vimportOpret.begin()[0] == EVAL_TOKENS)
return eval->Invalid("test-invalid-tokens-are-good!!");
else
return eval->Invalid("test-invalid-coins-are-good!!"); */
return true;
}
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
{
ImportProof proof;
CTransaction burnTx;
std::vector<CTxOut> payouts;
CAmount txfee = 10000, amount;
int32_t height, burnvout;
std::vector<CPubKey> publishers;
uint32_t targetCcid;
std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx;
uint256 payoutsHash, bindtxid, burntxid;
std::vector<uint8_t> rawproof;
std::vector<uint256> txids;
CPubKey destpub;
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "Validating import tx..., txid=" << importTx.GetHash().GetHex() << std::endl);
if (strcmp(SMART_CHAIN_SYMBOL, "CFEKDIMXY6") == 0 && chainActive.Height() <= 44693)
return true;
if (importTx.vout.size() < 2)
return Invalid("too-few-vouts");
// params
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
return Invalid("invalid-params");
// Control all aspects of this transaction
// It should not be at all malleable
if (ASSETCHAINS_SELFIMPORT!="PEGSCC" && MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication
return Invalid("non-canonical");
// burn params
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof))
return Invalid("invalid-burn-tx");
if (burnTx.vout.size() == 0)
return Invalid("invalid-burn-tx-no-vouts");
// check burned normal amount >= import amount && burned amount <= import amount + txfee (extra txfee is for miners and relaying, see GetImportCoinValue() func)
CAmount burnAmount = burnTx.vout.back().nValue;
if (burnAmount == 0)
return Invalid("invalid-burn-amount");
CAmount totalOut = 0;
for (auto v : importTx.vout)
if (!v.scriptPubKey.IsPayToCryptoCondition())
totalOut += v.nValue;
if (totalOut > burnAmount || totalOut < burnAmount - txfee)
return Invalid("payout-too-high-or-too-low");
// Check burntx shows correct outputs hash
if (payoutsHash != SerializeHash(payouts))
return Invalid("wrong-payouts");
if (targetCcid < HUSH_FIRSTFUNGIBLEID)
return Invalid("chain-not-fungible");
if ( targetCcid != 0xffffffff )
{
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
return Invalid("importcoin-wrong-chain");
if (!CheckMigration(this, importTx, burnTx, payouts, proof, rawproof))
return false; // eval->Invalid() is called in the func
}
else
{
TxProof merkleBranchProof;
if (!proof.IsMerkleBranch(merkleBranchProof))
return Invalid("invalid-import-proof-for-0xFFFFFFFF");
if ( targetSymbol == "BEAM" )
{
if ( ASSETCHAINS_BEAMPORT == 0 )
return Invalid("BEAM-import-without-port");
else if ( CheckBEAMimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
return Invalid("BEAM-import-failure");
}
else if ( targetSymbol == "CODA" )
{
if ( ASSETCHAINS_CODAPORT == 0 )
return Invalid("CODA-import-without-port");
else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 )
return Invalid("CODA-import-failure");
}
else if ( targetSymbol == "PEGSCC" )
{
if ( ASSETCHAINS_SELFIMPORT != "PEGSCC" )
return Invalid("PEGSCC-import-when-not PEGSCC");
// else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
// return Invalid("PEGSCC-import-failure");
}
else if ( targetSymbol == "PUBKEY" )
{
if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" )
return Invalid("PUBKEY-import-when-not PUBKEY");
else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
return Invalid("PUBKEY-import-failure");
}
else
{
if ( targetSymbol != ASSETCHAINS_SELFIMPORT )
return Invalid("invalid-gateway-import-coin");
else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 )
return Invalid("GATEWAY-import-failure");
}
}
// return Invalid("test-invalid");
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Valid import tx! txid=" << importTx.GetHash().GetHex() << std::endl);
return Valid();
}