Duke
4 months ago
26 changed files with 52 additions and 8018 deletions
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,764 +0,0 @@ |
|||
// Copyright (c) 2016-2023 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 (hush_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 (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(); |
|||
} |
File diff suppressed because it is too large
@ -1,8 +0,0 @@ |
|||
echo pricescc.so |
|||
gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp |
|||
echo prices |
|||
cd games |
|||
gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES ../gamescc.cpp -lncurses -lcurl -o prices |
|||
cd .. |
|||
|
|||
|
@ -1,7 +0,0 @@ |
|||
echo gamescc.so with tetris |
|||
gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp |
|||
echo tetris dapp |
|||
cd games |
|||
gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE ../gamescc.cpp -lncurses -lcurl -o tetris |
|||
cd .. |
|||
|
@ -1,425 +0,0 @@ |
|||
// Copyright (c) 2016-2023 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 "crosschain.h" |
|||
#include "importcoin.h" |
|||
#include "cc/utils.h" |
|||
#include "coins.h" |
|||
#include "hash.h" |
|||
#include "script/cc.h" |
|||
#include "primitives/transaction.h" |
|||
#include "core_io.h" |
|||
#include "script/sign.h" |
|||
#include "wallet/wallet.h" |
|||
|
|||
#include "cc/CCinclude.h" |
|||
|
|||
int32_t hush_nextheight(); |
|||
|
|||
// makes import tx for either coins or tokens
|
|||
CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride) |
|||
{ |
|||
//std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
|
|||
CScript scriptSig; |
|||
|
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); |
|||
if (mtx.fOverwintered) |
|||
mtx.nExpiryHeight = 0; |
|||
mtx.vout = payouts; |
|||
if (mtx.vout.size() == 0) |
|||
return CTransaction(mtx); |
|||
|
|||
// add special import tx vin:
|
|||
scriptSig << E_MARSHAL(ss << EVAL_IMPORTCOIN); // simple payload for coins
|
|||
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), scriptSig)); |
|||
|
|||
if (nExpiryHeightOverride != 0) |
|||
mtx.nExpiryHeight = nExpiryHeightOverride; //this is for validation code, to make a tx used for validating the import tx
|
|||
|
|||
auto importData = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); // added evalcode to differentiate importdata from token opret
|
|||
// if it is tokens:
|
|||
vscript_t vopret; |
|||
GetOpReturnData(mtx.vout.back().scriptPubKey, vopret); |
|||
|
|||
if (!vopret.empty()) { |
|||
std::vector<uint8_t> vorigpubkey; |
|||
uint8_t funcId; |
|||
std::vector <std::pair<uint8_t, vscript_t>> oprets; |
|||
std::string name, desc; |
|||
|
|||
if (DecodeTokenCreateOpRet(mtx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 'c') { // parse token 'c' opret
|
|||
mtx.vout.pop_back(); //remove old token opret
|
|||
oprets.push_back(std::make_pair(OPRETID_IMPORTDATA, importData)); |
|||
mtx.vout.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make new token 'c' opret with importData
|
|||
} |
|||
else { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeImportCoinTransaction() incorrect token import opret" << std::endl); |
|||
} |
|||
} |
|||
else { //no opret in coin payouts
|
|||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData)); // import tx's opret now is in the vout's tail
|
|||
} |
|||
|
|||
return CTransaction(mtx); |
|||
} |
|||
|
|||
CTransaction MakePegsImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride) |
|||
{ |
|||
CMutableTransaction mtx; uint256 accounttxid,pegstxid,tokenid; CScript opret; CScript scriptSig; |
|||
|
|||
mtx=MakeImportCoinTransaction(proof,burnTx,payouts); |
|||
// for spending markers in import tx - to track account state
|
|||
accounttxid=burnTx.vin[0].prevout.hash; |
|||
mtx.vin.push_back(CTxIn(accounttxid,0,CScript())); |
|||
mtx.vin.push_back(CTxIn(accounttxid,1,CScript())); |
|||
return (mtx); |
|||
} |
|||
|
|||
|
|||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector<CTxOut> payouts, const std::vector<uint8_t> rawproof) |
|||
{ |
|||
std::vector<uint8_t> opret; |
|||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; // should mark burn opret to differentiate it from token opret
|
|||
ss << VARINT(targetCCid); |
|||
ss << targetSymbol; |
|||
ss << SerializeHash(payouts); |
|||
ss << rawproof); |
|||
return CTxOut(value, CScript() << OP_RETURN << opret); |
|||
} |
|||
|
|||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof, |
|||
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) |
|||
{ |
|||
std::vector<uint8_t> opret; |
|||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; |
|||
ss << VARINT(targetCCid); |
|||
ss << targetSymbol; |
|||
ss << SerializeHash(payouts); |
|||
ss << rawproof; |
|||
ss << bindtxid; |
|||
ss << publishers; |
|||
ss << txids; |
|||
ss << burntxid; |
|||
ss << height; |
|||
ss << burnvout; |
|||
ss << rawburntx; |
|||
ss << destpub; |
|||
ss << amount); |
|||
|
|||
return CTxOut(value, CScript() << OP_RETURN << opret); |
|||
} |
|||
|
|||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,std::string srcaddr, |
|||
std::string receipt) |
|||
{ |
|||
std::vector<uint8_t> opret; |
|||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; |
|||
ss << VARINT(targetCCid); |
|||
ss << targetSymbol; |
|||
ss << SerializeHash(payouts); |
|||
ss << rawproof; |
|||
ss << srcaddr; |
|||
ss << receipt); |
|||
return CTxOut(value, CScript() << OP_RETURN << opret); |
|||
} |
|||
|
|||
CTxOut MakeBurnOutput(CAmount value,uint32_t targetCCid,std::string targetSymbol,const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,uint256 pegstxid, |
|||
uint256 tokenid,CPubKey srcpub,int64_t amount,std::pair<int64_t,int64_t> account) |
|||
{ |
|||
std::vector<uint8_t> opret; |
|||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; |
|||
ss << VARINT(targetCCid); |
|||
ss << targetSymbol; |
|||
ss << SerializeHash(payouts); |
|||
ss << rawproof; |
|||
ss << pegstxid; |
|||
ss << tokenid; |
|||
ss << srcpub; |
|||
ss << amount; |
|||
ss << account); |
|||
return CTxOut(value, CScript() << OP_RETURN << opret); |
|||
} |
|||
|
|||
|
|||
bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx, std::vector<CTxOut> &payouts) |
|||
{ |
|||
if (importTx.vout.size() < 1) |
|||
return false; |
|||
|
|||
if ((!importTx.IsPegsImport() && importTx.vin.size() != 1) || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() incorrect import tx vin" << std::endl); |
|||
return false; |
|||
} |
|||
|
|||
std::vector<uint8_t> vImportData; |
|||
GetOpReturnData(importTx.vout.back().scriptPubKey, vImportData); |
|||
if (vImportData.empty()) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() no opret" << std::endl); |
|||
return false; |
|||
} |
|||
|
|||
if (vImportData.begin()[0] == EVAL_TOKENS) { // if it is tokens
|
|||
// get import data after token opret:
|
|||
std::vector<std::pair<uint8_t, vscript_t>> oprets; |
|||
std::vector<uint8_t> vorigpubkey; |
|||
std::string name, desc; |
|||
|
|||
if (DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 0) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not decode token opret" << std::endl); |
|||
return false; |
|||
} |
|||
|
|||
GetOpretBlob(oprets, OPRETID_IMPORTDATA, vImportData); // fetch import data after token opret
|
|||
for (std::vector<std::pair<uint8_t, vscript_t>>::const_iterator i = oprets.begin(); i != oprets.end(); i++) |
|||
if ((*i).first == OPRETID_IMPORTDATA) { |
|||
oprets.erase(i); // remove import data from token opret to restore original payouts:
|
|||
break; |
|||
} |
|||
|
|||
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end()-1); //exclude opret with import data
|
|||
payouts.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make original payouts token opret (without import data)
|
|||
} |
|||
else { |
|||
//payouts = std::vector<CTxOut>(importTx.vout.begin()+1, importTx.vout.end()); // see next
|
|||
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end() - 1); // skip opret; and it is now in the back
|
|||
} |
|||
|
|||
uint8_t evalCode; |
|||
bool retcode = E_UNMARSHAL(vImportData, ss >> evalCode; ss >> proof; ss >> burnTx); |
|||
if (!retcode) |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not unmarshal import data" << std::endl); |
|||
return retcode; |
|||
} |
|||
|
|||
|
|||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t>&rawproof) |
|||
{ |
|||
std::vector<uint8_t> vburnOpret; uint32_t ccid = 0; |
|||
uint8_t evalCode; |
|||
|
|||
if (burnTx.vout.size() == 0) |
|||
return false; |
|||
|
|||
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret); |
|||
if (vburnOpret.empty()) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal burn tx: empty burn opret" << std::endl); |
|||
return false; |
|||
} |
|||
|
|||
if (vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
|
|||
std::vector<std::pair<uint8_t, vscript_t>> oprets; |
|||
uint256 tokenid; |
|||
uint8_t evalCodeInOpret; |
|||
std::vector<CPubKey> voutTokenPubkeys; |
|||
|
|||
if (DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 't') |
|||
return false; |
|||
|
|||
//skip token opret:
|
|||
GetOpretBlob(oprets, OPRETID_BURNDATA, vburnOpret); // fetch burnOpret after token opret
|
|||
if (vburnOpret.empty()) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal token burn tx: empty burn opret for tokenid=" << tokenid.GetHex() << std::endl); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
if (vburnOpret.begin()[0] == EVAL_IMPORTCOIN) { |
|||
uint8_t evalCode; |
|||
bool isEof = true; |
|||
return E_UNMARSHAL(vburnOpret, ss >> evalCode; |
|||
ss >> VARINT(*targetCCid); |
|||
ss >> targetSymbol; |
|||
ss >> payoutsHash; |
|||
ss >> rawproof; isEof = ss.eof();) || !isEof; // if isEof == false it means we have successfully read the vars upto 'rawproof'
|
|||
// and it might be additional data further that we do not need here so we allow !isEof
|
|||
} |
|||
else { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() invalid eval code in opret" << std::endl); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt) |
|||
{ |
|||
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true; |
|||
std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; |
|||
uint8_t evalCode; |
|||
|
|||
if (burnTx.vout.size() == 0) return false; |
|||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); |
|||
return (E_UNMARSHAL(burnOpret, ss >> evalCode; |
|||
ss >> VARINT(targetCCid); |
|||
ss >> targetSymbol; |
|||
ss >> payoutsHash; |
|||
ss >> rawproof; |
|||
ss >> srcaddr; |
|||
ss >> receipt)); |
|||
} |
|||
|
|||
bool UnmarshalBurnTx(const CTransaction burnTx,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) |
|||
{ |
|||
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true; |
|||
uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; |
|||
uint8_t evalCode; |
|||
|
|||
if (burnTx.vout.size() == 0) return false; |
|||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); |
|||
return (E_UNMARSHAL(burnOpret, ss >> evalCode; |
|||
ss >> VARINT(targetCCid); |
|||
ss >> targetSymbol; |
|||
ss >> payoutsHash; |
|||
ss >> rawproof; |
|||
ss >> bindtxid; |
|||
ss >> publishers; |
|||
ss >> txids; |
|||
ss >> burntxid; |
|||
ss >> height; |
|||
ss >> burnvout; |
|||
ss >> rawburntx; |
|||
ss >> destpub; |
|||
ss >> amount)); |
|||
} |
|||
|
|||
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &pegstxid,uint256 &tokenid,CPubKey &srcpub, int64_t &amount,std::pair<int64_t,int64_t> &account) |
|||
{ |
|||
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true; |
|||
uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; |
|||
uint8_t evalCode; |
|||
|
|||
|
|||
if (burnTx.vout.size() == 0) return false; |
|||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); |
|||
return (E_UNMARSHAL(burnOpret, ss >> evalCode; |
|||
ss >> VARINT(targetCCid); |
|||
ss >> targetSymbol; |
|||
ss >> payoutsHash; |
|||
ss >> rawproof; |
|||
ss >> pegstxid; |
|||
ss >> tokenid; |
|||
ss >> srcpub; |
|||
ss >> amount; |
|||
ss >> account)); |
|||
} |
|||
|
|||
/*
|
|||
* Required by main |
|||
*/ |
|||
CAmount GetCoinImportValue(const CTransaction &tx) |
|||
{ |
|||
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; |
|||
bool isNewImportTx = false; |
|||
|
|||
if ((isNewImportTx = UnmarshalImportTx(tx, proof, burnTx, payouts))) { |
|||
if (burnTx.vout.size() > 0) { |
|||
vscript_t vburnOpret; |
|||
|
|||
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret); |
|||
if (vburnOpret.empty()) { |
|||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetCoinImportValue() empty burn opret" << std::endl); |
|||
return 0; |
|||
} |
|||
|
|||
if (isNewImportTx && vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
|
|||
|
|||
uint8_t evalCodeInOpret; |
|||
uint256 tokenid; |
|||
std::vector<CPubKey> voutTokenPubkeys; |
|||
std::vector<std::pair<uint8_t, vscript_t>> oprets; |
|||
|
|||
if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0) |
|||
return 0; |
|||
|
|||
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init as if no non-fungibles
|
|||
vscript_t vnonfungibleOpret; |
|||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret); |
|||
if (!vnonfungibleOpret.empty()) |
|||
nonfungibleEvalCode = vnonfungibleOpret.begin()[0]; |
|||
|
|||
// calc outputs for burn tx
|
|||
int64_t 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; |
|||
|
|||
return ccBurnOutputs + burnTx.vout.back().nValue; // total token burned value
|
|||
} |
|||
else |
|||
return burnTx.vout.back().nValue; // coin burned value
|
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
|
|||
/*
|
|||
* CoinImport is different enough from normal script execution that it's not worth |
|||
* making all the mods neccesary in the interpreter to do the dispatch correctly. |
|||
*/ |
|||
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state) |
|||
{ |
|||
auto pc = scriptSig.begin(); |
|||
opcodetype opcode; |
|||
std::vector<uint8_t> evalScript; |
|||
|
|||
auto f = [&] () { |
|||
if (!scriptSig.GetOp(pc, opcode, evalScript)) |
|||
return false; |
|||
if (pc != scriptSig.end()) |
|||
return false; |
|||
if (evalScript.size() == 0) |
|||
return false; |
|||
if (evalScript.begin()[0] != EVAL_IMPORTCOIN) |
|||
return false; |
|||
// Ok, all looks good so far...
|
|||
CC *cond = CCNewEval(evalScript); |
|||
bool out = checker.CheckEvalCondition(cond); |
|||
cc_free(cond); |
|||
return out; |
|||
}; |
|||
|
|||
return f() ? true : state.Invalid(false, 0, "invalid-coin-import"); |
|||
} |
|||
|
|||
|
|||
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight) |
|||
{ |
|||
uint256 burnHash = importTx.vin[0].prevout.hash; |
|||
//fprintf(stderr,"add tombstone.(%s)\n",burnHash.GetHex().c_str());
|
|||
CCoinsModifier modifier = inputs.ModifyCoins(burnHash); |
|||
modifier->nHeight = nHeight; |
|||
modifier->nVersion = 4;//1;
|
|||
modifier->vout.push_back(CTxOut(0, CScript() << OP_0)); |
|||
} |
|||
|
|||
|
|||
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs) |
|||
{ |
|||
uint256 burnHash = importTx.vin[0].prevout.hash; |
|||
//fprintf(stderr,"remove tombstone.(%s)\n",burnHash.GetHex().c_str());
|
|||
inputs.ModifyCoins(burnHash)->Clear(); |
|||
} |
|||
|
|||
|
|||
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs) |
|||
{ |
|||
uint256 burnHash = importTx.vin[0].prevout.hash; |
|||
//fprintf(stderr,"check tombstone.(%s) in %s\n",burnHash.GetHex().c_str(),importTx.GetHash().GetHex().c_str());
|
|||
return inputs.HaveCoins(burnHash); |
|||
} |
File diff suppressed because it is too large
@ -1,260 +0,0 @@ |
|||
// Copyright (c) 2016-2023 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
|
|||
|
|||
#include <cryptoconditions.h> |
|||
#include <gtest/gtest.h> |
|||
|
|||
#include "cc/eval.h" |
|||
#include "importcoin.h" |
|||
#include "base58.h" |
|||
#include "core_io.h" |
|||
#include "key.h" |
|||
#include "main.h" |
|||
#include "primitives/transaction.h" |
|||
#include "script/cc.h" |
|||
#include "script/interpreter.h" |
|||
#include "script/serverchecker.h" |
|||
#include "txmempool.h" |
|||
|
|||
#include "testutils.h" |
|||
|
|||
|
|||
extern Eval* EVAL_TEST; |
|||
|
|||
namespace TestCoinImport { |
|||
|
|||
|
|||
static uint8_t testNum = 0; |
|||
|
|||
class TestCoinImport : public ::testing::Test, public Eval { |
|||
public: |
|||
CMutableTransaction burnTx; std::vector<uint8_t> rawproof; |
|||
std::vector<CTxOut> payouts; |
|||
TxProof proof; |
|||
uint256 MoMoM; |
|||
CMutableTransaction importTx; |
|||
uint32_t testCcid = 2; |
|||
std::string testSymbol = "PIZZA"; |
|||
CAmount amount = 100; |
|||
|
|||
void SetImportTx() { |
|||
burnTx.vout.resize(0); |
|||
burnTx.vout.push_back(MakeBurnOutput(amount, testCcid, testSymbol, payouts,rawproof)); |
|||
importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts)); |
|||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
|||
} |
|||
|
|||
uint32_t GetAssetchainsCC() const { return testCcid; } |
|||
std::string GetAssetchainsSymbol() const { return testSymbol; } |
|||
|
|||
bool GetProofRoot(uint256 hash, uint256 &momom) const |
|||
{ |
|||
if (MoMoM.IsNull()) return false; |
|||
momom = MoMoM; |
|||
return true; |
|||
} |
|||
|
|||
|
|||
protected: |
|||
static void SetUpTestCase() { setupChain(); } |
|||
virtual void SetUp() { |
|||
ASSETCHAINS_CC = 1; |
|||
EVAL_TEST = this; |
|||
|
|||
std::vector<uint8_t> fakepk; |
|||
fakepk.resize(33); |
|||
fakepk.begin()[0] = testNum++; |
|||
payouts.push_back(CTxOut(amount, CScript() << fakepk << OP_CHECKSIG)); |
|||
SetImportTx(); |
|||
} |
|||
|
|||
|
|||
void TestRunCCEval(CMutableTransaction mtx) |
|||
{ |
|||
CTransaction importTx(mtx); |
|||
PrecomputedTransactionData txdata(importTx); |
|||
ServerTransactionSignatureChecker checker(&importTx, 0, 0, false, txdata); |
|||
CValidationState verifystate; |
|||
if (!VerifyCoinImport(importTx.vin[0].scriptSig, checker, verifystate)) |
|||
printf("TestRunCCEval: %s\n", verifystate.GetRejectReason().data()); |
|||
} |
|||
}; |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testProcessImportThroughPipeline) |
|||
{ |
|||
CValidationState mainstate; |
|||
CTransaction tx(importTx); |
|||
|
|||
// first should work
|
|||
acceptTxFail(tx); |
|||
|
|||
// should fail in mempool
|
|||
ASSERT_FALSE(acceptTx(tx, mainstate)); |
|||
EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); |
|||
|
|||
// should be in persisted UTXO set
|
|||
generateBlock(); |
|||
ASSERT_FALSE(acceptTx(tx, mainstate)); |
|||
EXPECT_EQ("already have coins", mainstate.GetRejectReason()); |
|||
ASSERT_TRUE(pcoinsTip->HaveCoins(tx.GetHash())); |
|||
|
|||
// Now disconnect the block
|
|||
CValidationState invalstate; |
|||
if (!InvalidateBlock(invalstate, chainActive.Tip())) { |
|||
FAIL() << invalstate.GetRejectReason(); |
|||
} |
|||
ASSERT_FALSE(pcoinsTip->HaveCoins(tx.GetHash())); |
|||
|
|||
// should be back in mempool
|
|||
ASSERT_FALSE(acceptTx(tx, mainstate)); |
|||
EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testImportTombstone) |
|||
{ |
|||
CValidationState mainstate; |
|||
// By setting an unspendable output, there will be no addition to UTXO
|
|||
// Nonetheless, we dont want to be able to import twice
|
|||
payouts[0].scriptPubKey = CScript() << OP_RETURN; |
|||
SetImportTx(); |
|||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
|||
CTransaction tx(importTx); |
|||
|
|||
// first should work
|
|||
acceptTxFail(tx); |
|||
|
|||
// should be in persisted UTXO set
|
|||
generateBlock(); |
|||
ASSERT_FALSE(acceptTx(tx, mainstate)); |
|||
EXPECT_EQ("import tombstone exists", mainstate.GetRejectReason()); |
|||
ASSERT_TRUE(pcoinsTip->HaveCoins(burnTx.GetHash())); |
|||
|
|||
// Now disconnect the block
|
|||
CValidationState invalstate; |
|||
if (!InvalidateBlock(invalstate, chainActive.Tip())) { |
|||
FAIL() << invalstate.GetRejectReason(); |
|||
} |
|||
// Tombstone should be gone from utxo set
|
|||
ASSERT_FALSE(pcoinsTip->HaveCoins(burnTx.GetHash())); |
|||
|
|||
// should be back in mempool
|
|||
ASSERT_FALSE(acceptTx(tx, mainstate)); |
|||
EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testNoVouts) |
|||
{ |
|||
importTx.vout.resize(0); |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("too-few-vouts", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testInvalidParams) |
|||
{ |
|||
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << 'a'); |
|||
importTx.vin[0].scriptSig = CScript() << payload; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("invalid-params", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testNonCanonical) |
|||
{ |
|||
importTx.nLockTime = 10; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("non-canonical", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testInvalidBurnOutputs) |
|||
{ |
|||
burnTx.vout.resize(0); |
|||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
|||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); |
|||
TestRunCCEval(tx); |
|||
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testInvalidBurnParams) |
|||
{ |
|||
burnTx.vout.back().scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid)); |
|||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
|||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); |
|||
TestRunCCEval(tx); |
|||
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testWrongChainId) |
|||
{ |
|||
testCcid = 0; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("importcoin-wrong-chain", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testInvalidBurnAmount) |
|||
{ |
|||
burnTx.vout.back().nValue = 0; |
|||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
|||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); |
|||
TestRunCCEval(tx); |
|||
EXPECT_EQ("invalid-burn-amount", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testPayoutTooHigh) |
|||
{ |
|||
importTx.vout[1].nValue = 101; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("payout-too-high", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testAmountInOpret) |
|||
{ |
|||
importTx.vout[0].nValue = 1; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("non-canonical", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
|
|||
TEST_F(TestCoinImport, testInvalidPayouts) |
|||
{ |
|||
importTx.vout[1].nValue = 40; |
|||
importTx.vout.push_back(importTx.vout[0]); |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("wrong-payouts", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testCouldntLoadMomom) |
|||
{ |
|||
MoMoM.SetNull(); |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("coudnt-load-momom", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testMomomCheckFail) |
|||
{ |
|||
MoMoM.SetNull(); |
|||
MoMoM.begin()[0] = 1; |
|||
TestRunCCEval(importTx); |
|||
EXPECT_EQ("momom-check-fail", state.GetRejectReason()); |
|||
} |
|||
|
|||
|
|||
TEST_F(TestCoinImport, testGetCoinImportValue) |
|||
{ |
|||
ASSERT_EQ(100, GetCoinImportValue(importTx)); |
|||
} |
|||
|
|||
} /* namespace TestCoinImport */ |
Loading…
Reference in new issue