Delete more CCs #381
This commit is contained in:
@@ -28,18 +28,9 @@
|
||||
#include "core_io.h"
|
||||
#define FAUCET2SIZE COIN
|
||||
#define EVAL_FAUCET2 EVAL_FIRSTUSER
|
||||
#ifdef BUILD_ROGUE
|
||||
#define EVAL_ROGUE 17
|
||||
std::string MYCCLIBNAME = (char *)"rogue";
|
||||
|
||||
#elif BUILD_CUSTOMCC
|
||||
#ifdef BUILD_CUSTOMCC
|
||||
#include "customcc.h"
|
||||
|
||||
#else
|
||||
#define EVAL_SUDOKU 17
|
||||
#define EVAL_MUSIG 18
|
||||
#define EVAL_DILITHIUM 19
|
||||
std::string MYCCLIBNAME = (char *)"sudoku";
|
||||
#endif
|
||||
|
||||
#ifndef BUILD_GAMESCC
|
||||
@@ -147,74 +138,16 @@ int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe
|
||||
|
||||
std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,cclibpk; CScript opret;
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
cclibpk = GetUnspendable(cp,0);
|
||||
if ( AddNormalinputs2(mtx,funds+txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params)
|
||||
{
|
||||
CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,cclibpk; int64_t funds,txfee=0,inputs,CCchange=0,nValue=FAUCET2SIZE; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash;
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
if ( funcid == 'F' )
|
||||
{
|
||||
if ( cJSON_GetArraySize(params) > 0 )
|
||||
{
|
||||
funds = (int64_t)jdouble(jitem(params,0),0)*COIN + 0.0000000049;
|
||||
return(Faucet2Fund(cp,0,funds));
|
||||
} else return("");
|
||||
}
|
||||
else if ( funcid != 'G' )
|
||||
return("");
|
||||
cclibpk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60,cp->unspendableCCaddr,1)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET2,CCchange,cclibpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL));
|
||||
j = rand() & 0xfffffff;
|
||||
for (i=0; i<1000000; i++,j++)
|
||||
{
|
||||
tmpmtx = mtx;
|
||||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET2 << (uint8_t)'G' << j));
|
||||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 )
|
||||
{
|
||||
len >>= 1;
|
||||
decode_hex(buf,len,(char *)rawhex.c_str());
|
||||
hash = bits256_doublesha256(0,buf,len);
|
||||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 )
|
||||
{
|
||||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL));
|
||||
return(rawhex);
|
||||
}
|
||||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL));
|
||||
return("");
|
||||
} else fprintf(stderr,"cant find faucet inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
UniValue cclib_error(UniValue &result,const char *errorstr)
|
||||
{
|
||||
result.push_back(Pair("status","error"));
|
||||
result.push_back(Pair("error",errorstr));
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -227,12 +160,7 @@ uint256 juint256(cJSON *obj)
|
||||
|
||||
int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len)
|
||||
{
|
||||
char *hexstr;
|
||||
if ( (hexstr= jstr(item,0)) != 0 && is_hexstr(hexstr,0) == len*2 )
|
||||
{
|
||||
decode_hex(hash32,len,hexstr);
|
||||
return(0);
|
||||
} else return(-1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,679 +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
|
||||
#ifndef HEIR_VALIDATE_H
|
||||
#define HEIR_VALIDATE_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include "CCHeir.h"
|
||||
|
||||
#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos)
|
||||
|
||||
// makes coin initial tx opret
|
||||
vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo);
|
||||
vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan);
|
||||
|
||||
uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan);
|
||||
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false);
|
||||
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
|
||||
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false);
|
||||
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false);
|
||||
|
||||
inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
|
||||
inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); }
|
||||
|
||||
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins
|
||||
class CoinHelper {
|
||||
public:
|
||||
|
||||
static uint8_t getMyEval() { return EVAL_HEIR; }
|
||||
static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
|
||||
return AddNormalinputs(mtx, ownerPubkey, total, maxinputs);
|
||||
}
|
||||
|
||||
static CScript makeCreateOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) {
|
||||
return CScript() << OP_RETURN << EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo);
|
||||
}
|
||||
static CScript makeAddOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan);
|
||||
}
|
||||
static CScript makeClaimOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan);
|
||||
}
|
||||
static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
|
||||
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
|
||||
}
|
||||
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
|
||||
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
|
||||
} */
|
||||
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_HEIR);
|
||||
return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr)
|
||||
{
|
||||
uint8_t mypriv[32];
|
||||
Myprivkey(mypriv);
|
||||
CCaddr1of2set(cp, ownerPubkey, heirPubkey,mypriv, coinaddr);
|
||||
memset(mypriv,0,sizeof(mypriv));
|
||||
}
|
||||
};
|
||||
|
||||
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens
|
||||
class TokenHelper {
|
||||
public:
|
||||
static uint8_t getMyEval() { return EVAL_TOKENS; }
|
||||
static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
|
||||
/*
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_TOKENS);
|
||||
return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CScript makeCreateOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo)));
|
||||
}
|
||||
static CScript makeAddOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)));
|
||||
}
|
||||
static CScript makeClaimOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)));
|
||||
}
|
||||
|
||||
static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
|
||||
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
|
||||
}
|
||||
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
|
||||
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
|
||||
} */
|
||||
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_HEIR);
|
||||
return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
|
||||
}
|
||||
|
||||
static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) {
|
||||
uint8_t mypriv[32];
|
||||
Myprivkey(mypriv);
|
||||
CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, mypriv, coinaddr);
|
||||
memset(mypriv,0,sizeof(mypriv));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern:
|
||||
* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators
|
||||
* (These validators are derived from the class CValidatorBase).
|
||||
*
|
||||
* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn.
|
||||
* Or, vin validator may check if this vin depicts correctly to the CC contract's address.
|
||||
*
|
||||
* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class.
|
||||
* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it.
|
||||
* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false.
|
||||
*
|
||||
* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied
|
||||
* (see constructors of both CInputValidator and COutputValidator)
|
||||
*/
|
||||
|
||||
/**
|
||||
* base class for all validators
|
||||
*/
|
||||
class CValidatorBase
|
||||
{
|
||||
public:
|
||||
CValidatorBase(CCcontract_info* cp) : m_cp(cp) {}
|
||||
virtual bool isVinValidator() const = 0;
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const = 0;
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0;
|
||||
|
||||
protected:
|
||||
CCcontract_info * m_cp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for classes which identify vins as normal or cc inputs
|
||||
*/
|
||||
class CInputIdentifierBase
|
||||
{
|
||||
public:
|
||||
CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {}
|
||||
virtual std::string inputName() const = 0;
|
||||
virtual bool identifyInput(CTxIn vin) const = 0;
|
||||
protected:
|
||||
CCcontract_info * m_cp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates an array containing rows of validators
|
||||
* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts
|
||||
* this validation plan is used for validating tx inputs
|
||||
*/
|
||||
template <typename TValidatorBase>
|
||||
class CInputValidationPlan
|
||||
{
|
||||
using ValidatorsRow = std::vector<TValidatorBase*>;
|
||||
|
||||
public:
|
||||
|
||||
// Pushes a row of validators for validating a vin or vout
|
||||
// @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs")
|
||||
// @param pargs parameter pack of zero or more pointer to validator objects
|
||||
// Why pointers? because we store the base class in validators' row and then call its virtual functions
|
||||
template <typename TValidatorBaseX, typename... ARGS>
|
||||
void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
|
||||
{
|
||||
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
|
||||
m_arrayValidators.push_back(std::make_pair(identifier, vValidators));
|
||||
}
|
||||
|
||||
// validate tx inputs and corresponding prev tx vouts
|
||||
bool validate(const CTransaction& tx, Eval* eval)
|
||||
{
|
||||
std::string message = "<empty>";
|
||||
//std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl;
|
||||
|
||||
int32_t ival = 0;
|
||||
int32_t iv = 0;
|
||||
int32_t numv = tx.vin.size();
|
||||
int32_t numValidators = m_arrayValidators.size();
|
||||
|
||||
// run over vins:
|
||||
while (iv < numv && ival < numValidators) {
|
||||
|
||||
int32_t identifiedCount = 0;
|
||||
CInputIdentifierBase *identifier = m_arrayValidators[ival].first;
|
||||
// check if this is 'our' input:
|
||||
while (iv < numv && identifier->identifyInput(tx.vin[iv])) {
|
||||
|
||||
// get prev tx:
|
||||
CTransaction prevTx, *pPrevTxOrNull = NULL;
|
||||
uint256 hashBlock;
|
||||
|
||||
if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) {
|
||||
std::ostringstream stream;
|
||||
stream << "can't find vinTx for vin=" << iv << ".";
|
||||
return eval->Invalid(stream.str().c_str());
|
||||
}
|
||||
pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present)
|
||||
|
||||
// exec 'validators' from validator row of ival index, for tx.vin[iv]
|
||||
if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) {
|
||||
std::ostringstream stream;
|
||||
stream << "invalid tx vin[" << iv << "]:" << message;
|
||||
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
|
||||
}
|
||||
|
||||
identifiedCount++; // how many vins we identified
|
||||
iv++; // advance to the next vin
|
||||
}
|
||||
|
||||
// CInputValidationPlan treats that there must be at least one identified vin for configured validators' row
|
||||
// like in 'vin.0: normal input'
|
||||
if (identifiedCount == 0) {
|
||||
std::ostringstream stream;
|
||||
stream << "can't find required vins for " << identifier->inputName() << ".";
|
||||
return eval->Invalid(stream.str().c_str());
|
||||
}
|
||||
|
||||
ival++; // advance to the next validator row
|
||||
// and it will try the same vin with the new CInputIdentifierBase and validators row
|
||||
}
|
||||
|
||||
// validation is successful if all validators have been used (i.e. ival = numValidators)
|
||||
if (ival < numValidators) {
|
||||
std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl;
|
||||
return eval->Invalid("incorrect tx structure: not all required vins are present.");
|
||||
}
|
||||
|
||||
//std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
|
||||
bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const
|
||||
{
|
||||
// check boundaries:
|
||||
if (ival < 0 || ival >= m_arrayValidators.size()) {
|
||||
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param ival index";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iv < 0 || iv >= pTx->vin.size()) {
|
||||
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param iv index";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get requested row of validators:
|
||||
ValidatorsRow vValidators = m_arrayValidators[ival].second;
|
||||
|
||||
//std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl;
|
||||
|
||||
for (auto v : vValidators) {
|
||||
bool result;
|
||||
|
||||
if (v->isVinValidator())
|
||||
// validate this vin and previous vout:
|
||||
result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage);
|
||||
else
|
||||
// if it is vout validator pass the previous tx vout:
|
||||
result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return true; // validation OK
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//std::map<CInputIdentifierBase*, ValidatorsRow> m_arrayValidators;
|
||||
std::vector< std::pair<CInputIdentifierBase*, ValidatorsRow> > m_arrayValidators;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates an array containing rows of validators
|
||||
* Each row is a vector of validators (zero is possible) for validating vouts
|
||||
* this validation plan is used for validating tx outputs
|
||||
*/
|
||||
template <typename TValidatorBase>
|
||||
class COutputValidationPlan
|
||||
{
|
||||
using ValidatorsRow = std::vector<TValidatorBase*>;
|
||||
|
||||
public:
|
||||
// Pushes a row of validators for validating a vout
|
||||
// @param ivout index to vout to validate
|
||||
// @param pargs parameter pack of zero or more pointer to validator objects
|
||||
// Why pointers? because we store base class and call its virtual functions
|
||||
|
||||
template <typename TValidatorBaseX, typename... ARGS>
|
||||
void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
|
||||
{
|
||||
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
|
||||
m_arrayValidators.push_back(std::make_pair(ivout, vValidators));
|
||||
}
|
||||
|
||||
// validate tx outputs
|
||||
bool validate(const CTransaction& tx, Eval* eval)
|
||||
{
|
||||
std::string message = "<empty>";
|
||||
//std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl;
|
||||
|
||||
int32_t ival = 0;
|
||||
int32_t numVouts = tx.vout.size();
|
||||
int32_t numValidators = m_arrayValidators.size();
|
||||
|
||||
// run over vouts:
|
||||
while (ival < numValidators) {
|
||||
|
||||
int32_t ivout = m_arrayValidators[ival].first;
|
||||
if (ivout >= numVouts) {
|
||||
std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl;
|
||||
return eval->Invalid("incorrect tx structure: not all required vouts are present.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// exec 'validators' from validator row of ival index, for tx.vout[ivout]
|
||||
if (!execValidatorsInRow(&tx, ivout, ival, message)) {
|
||||
std::ostringstream stream;
|
||||
stream << "invalid tx vout[" << ivout << "]:" << message;
|
||||
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
|
||||
}
|
||||
}
|
||||
ival++; // advance to the next vout
|
||||
}
|
||||
//std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
|
||||
bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const
|
||||
{
|
||||
// check boundaries:
|
||||
if (ival < 0 || ival >= m_arrayValidators.size()) {
|
||||
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param ival index";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iv < 0 || iv >= pTx->vout.size()) {
|
||||
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param iv index";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get requested row of validators:
|
||||
ValidatorsRow vValidators = m_arrayValidators[ival].second;
|
||||
|
||||
//std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl;
|
||||
|
||||
for (auto v : vValidators) {
|
||||
|
||||
if (!v->isVinValidator()) {
|
||||
// if this is a 'in' validation plan then pass the previous tx vout:
|
||||
bool result = v->validateVout(pTx->vout[iv], iv, refMessage);
|
||||
if (!result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return true; // validation OK
|
||||
}
|
||||
|
||||
private:
|
||||
//std::map<int32_t, ValidatorsRow> m_mapValidators;
|
||||
std::vector< std::pair<int32_t, ValidatorsRow> > m_arrayValidators;
|
||||
|
||||
};
|
||||
|
||||
class CNormalInputIdentifier : CInputIdentifierBase {
|
||||
public:
|
||||
CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
|
||||
virtual std::string inputName() const { return std::string("normal input"); }
|
||||
virtual bool identifyInput(CTxIn vin) const {
|
||||
return !IsCCInput(vin.scriptSig);
|
||||
}
|
||||
};
|
||||
|
||||
class CCCInputIdentifier : CInputIdentifierBase {
|
||||
public:
|
||||
CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
|
||||
virtual std::string inputName() const { return std::string("CC input"); }
|
||||
virtual bool identifyInput(CTxIn vin) const {
|
||||
return IsCCInput(vin.scriptSig);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Validates 1of2address for vout (may be used for either this or prev tx)
|
||||
*/
|
||||
template <class Helper> class CCC1of2AddressValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") :
|
||||
m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl;
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = m_customMessage + std::string(" invalid opreturn format");
|
||||
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
char shouldBeAddr[65], ccAddr[65];
|
||||
|
||||
//GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey);
|
||||
Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey);
|
||||
|
||||
if (vout.scriptPubKey.IsPayToCryptoCondition()) {
|
||||
if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) {
|
||||
//std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr");
|
||||
}
|
||||
|
||||
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
|
||||
return false;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return false; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
std::string m_customMessage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Validates if this is vout to owner or heir from opret (funding or change)
|
||||
*/
|
||||
template <class Helper> class CMyPubkeyVoutValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals)
|
||||
: m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl;
|
||||
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl;
|
||||
|
||||
// get both pubkeys:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
CScript ownerScript;
|
||||
CScript heirScript;
|
||||
if (m_checkNormals) { //not used, incorrect check, too strict
|
||||
ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
|
||||
heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
|
||||
}
|
||||
else {
|
||||
ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
|
||||
heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
|
||||
}
|
||||
|
||||
// recreate scriptPubKey for owner and heir and compare it with that of the vout to check:
|
||||
if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) {
|
||||
// this is vout to owner or heir addr:
|
||||
//std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl;
|
||||
message = std::string("invalid pubkey");
|
||||
return false;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
//uint256 m_lasttxid;
|
||||
bool m_checkNormals;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime)
|
||||
*/
|
||||
template <class Helper> class CHeirSpendValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan)
|
||||
: m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl;
|
||||
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
// get heir pubkey:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t numblocks;
|
||||
int64_t durationSec = CCduration(numblocks, m_latesttxid);
|
||||
|
||||
// recreate scriptPubKey for heir and compare it with that of the vout:
|
||||
if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) {
|
||||
// this is the heir is trying to spend
|
||||
if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) {
|
||||
message = "heir is not allowed yet to spend funds";
|
||||
std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// heir is allowed to spend
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl;
|
||||
|
||||
// this is not heir:
|
||||
return true;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
uint256 m_latesttxid;
|
||||
uint8_t m_isHeirSpendingBegan;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates this opreturn and compares it with the opreturn from the previous tx
|
||||
*/
|
||||
template <class Helper> class COpRetValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
COpRetValidator(CCcontract_info* cp, CScript opret)
|
||||
: m_fundingOpretScript(opret), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "COpRetValidator::validateVout() entered" << std::endl;
|
||||
|
||||
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
|
||||
uint8_t dummyIsHeirSpendingBegan;
|
||||
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true);
|
||||
if (initialFuncId == 0) {
|
||||
message = std::string("invalid initial tx opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
// validation rules:
|
||||
if (!isMyFuncId(funcId)) {
|
||||
message = std::string("invalid funcid in opret");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeid(Helper) == typeid(TokenHelper)) {
|
||||
if (tokenid != initialTokenid) {
|
||||
message = std::string("invalid tokenid in opret");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* marker spending prevention validator,
|
||||
* returns false if for tx with funcid=F vout.1 is being tried to spend
|
||||
*/
|
||||
template <class Helper> class CMarkerValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CMarkerValidator(CCcontract_info* cp)
|
||||
: CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return true; } // this is vin validator
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const {
|
||||
|
||||
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
|
||||
uint8_t dummyIsHeirSpendingBegan;
|
||||
|
||||
//std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl;
|
||||
if (prevVout.size() > 0) {
|
||||
|
||||
// get funcId for prev tx:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
|
||||
|
||||
//std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl;
|
||||
|
||||
if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout
|
||||
message = std::string("spending marker not allowed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* empty validator always returns true
|
||||
*/
|
||||
template <class Helper> class CNullValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CNullValidator(CCcontract_info* cp)
|
||||
: CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user