blackjok3rtt
6 years ago
committed by
GitHub
66 changed files with 7427 additions and 1705 deletions
@ -0,0 +1,768 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
#include "CCtokens.h" |
|||
|
|||
/* TODO: correct this:
|
|||
----------------------------- |
|||
The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively. |
|||
|
|||
This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract. |
|||
|
|||
//vin.0: normal input
|
|||
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
|||
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
|
|||
//vout.0: remaining amount of bid to unspendable
|
|||
//vout.1: vin.1 value to signer of vin.2
|
|||
//vout.2: vin.2 tokenoshis to original pubkey
|
|||
//vout.3: CC output for tokenoshis change (if any)
|
|||
//vout.4: normal output for change (if any)
|
|||
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey]
|
|||
ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits); |
|||
|
|||
Yes, this is quite confusing... |
|||
|
|||
In ValudateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. |
|||
|
|||
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. |
|||
------------------------------ |
|||
*/ |
|||
|
|||
|
|||
// NOTE: this inital tx won't be used by other contract
|
|||
// for tokens to be used there should be at least one 't' tx with other contract's custom opret
|
|||
CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_TOKENS; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); |
|||
return(opret); |
|||
} |
|||
|
|||
// this is for other contracts which use tokens and build customized extra payloads to token's opret:
|
|||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload) |
|||
{ |
|||
CScript opret; |
|||
uint8_t tokenFuncId = 't'; |
|||
uint8_t evalCodeInOpret = EVAL_TOKENS; |
|||
|
|||
tokenid = revuint256(tokenid); |
|||
|
|||
uint8_t ccType = 0; |
|||
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) |
|||
ccType = voutPubkeys.size(); |
|||
|
|||
std::vector<uint8_t> vpayload; |
|||
GetOpReturnData(payload, vpayload); |
|||
|
|||
opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \ |
|||
if (ccType >= 1) ss << voutPubkeys[0]; \ |
|||
if (ccType == 2) ss << voutPubkeys[1]; \ |
|||
if (vpayload.size() > 0) ss << vpayload;); |
|||
|
|||
|
|||
// "error 64: scriptpubkey":
|
|||
// if (payload.size() > 0)
|
|||
// opret += payload;
|
|||
|
|||
// error 64: scriptpubkey:
|
|||
// CScript opretPayloadNoOpcode(vpayload);
|
|||
// return opret + opretPayloadNoOpcode;
|
|||
|
|||
// how to attach payload without re-serialization:
|
|||
// sig_aborted:
|
|||
// opret.resize(opret.size() + vpayload.size());
|
|||
// CScript::iterator it = opret.begin() + opret.size();
|
|||
// for (int i = 0; i < vpayload.size(); i++, it++)
|
|||
// *it = vpayload[i];
|
|||
|
|||
return opret; |
|||
} |
|||
|
|||
// overload for compatibility
|
|||
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload) |
|||
{ |
|||
return EncodeTokenOpRet(tokenid, voutPubkeys, payload); |
|||
} |
|||
|
|||
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t dummyEvalcode, funcid, *script; |
|||
|
|||
GetOpReturnData(scriptPubKey, vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' ) |
|||
{ |
|||
if ( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 ) |
|||
return(funcid); |
|||
} |
|||
return (uint8_t)0; |
|||
} |
|||
|
|||
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) |
|||
{ |
|||
std::vector<uint8_t> vopret, extra, dummyPubkey; |
|||
uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; |
|||
std::string dummyName; std::string dummyDescription; |
|||
CPubKey voutPubkey1, voutPubkey2; |
|||
|
|||
GetOpReturnData(scriptPubKey, vopret); |
|||
script = (uint8_t *)vopret.data(); |
|||
tokenid = zeroid; |
|||
|
|||
if (script != 0 && vopret.size() > 2) |
|||
{ |
|||
// NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
|
|||
bool isEof = true; |
|||
|
|||
evalCode = script[0]; |
|||
if (evalCode != EVAL_TOKENS) |
|||
return (uint8_t)0; |
|||
|
|||
funcId = script[1]; |
|||
//fprintf(stderr,"decode.[%c]\n",funcId);
|
|||
|
|||
switch( funcId ) |
|||
{ |
|||
case 'c': |
|||
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription); |
|||
//break;
|
|||
case 't': |
|||
//not used yet: case 'l':
|
|||
// NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error'
|
|||
if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; if (ccType >= 1) ss >> voutPubkey1; if (ccType == 2) ss >> voutPubkey2; isEof = ss.eof(); vopretExtra = std::vector<uint8_t>(ss.begin(), ss.end())) |
|||
|| !isEof) |
|||
{ |
|||
|
|||
if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType
|
|||
std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; |
|||
return (uint8_t)0; |
|||
} |
|||
|
|||
// add verification pubkeys:
|
|||
voutPubkeys.clear(); |
|||
if (voutPubkey1.IsValid()) |
|||
voutPubkeys.push_back(voutPubkey1); |
|||
if (voutPubkey2.IsValid()) |
|||
voutPubkeys.push_back(voutPubkey2); |
|||
|
|||
tokenid = revuint256(tokenid); |
|||
return(funcId); |
|||
} |
|||
std::cerr << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; |
|||
return (uint8_t)0; |
|||
|
|||
default: |
|||
std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl; |
|||
return (uint8_t)0; |
|||
} |
|||
} |
|||
else { |
|||
std::cerr << "DecodeTokenOpRet() empty opret, could not parse" << std::endl; |
|||
} |
|||
return (uint8_t)0; |
|||
} |
|||
|
|||
|
|||
|
|||
// tx validation
|
|||
bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) |
|||
{ |
|||
static uint256 zero; |
|||
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; |
|||
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; |
|||
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; |
|||
std::vector<uint8_t> vopretExtra, tmporigpubkey, ignorepubkey; |
|||
uint8_t funcid, evalCodeInOpret; |
|||
char destaddr[64], origaddr[64], CCaddr[64]; |
|||
std::vector<CPubKey> voutTokenPubkeys; |
|||
|
|||
//return true;
|
|||
|
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
outputs = inputs = 0; |
|||
preventCCvins = preventCCvouts = -1; |
|||
|
|||
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0) |
|||
return eval->Invalid("TokenValidate: invalid opreturn payload"); |
|||
|
|||
fprintf(stderr, "TokensValidate (%c) evalcode=0x%0x\n", funcid, cp->evalcode); |
|||
|
|||
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) |
|||
return eval->Invalid("cant find token create txid"); |
|||
else if (IsCCInput(tx.vin[0].scriptSig) != 0) |
|||
return eval->Invalid("illegal token vin0"); |
|||
else if (numvouts < 1) |
|||
return eval->Invalid("no vouts"); |
|||
else if (funcid != 'c') |
|||
{ |
|||
if (tokenid == zeroid) |
|||
return eval->Invalid("illegal tokenid"); |
|||
else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) { |
|||
if (!eval->Valid()) |
|||
return false; //TokenExactAmounts must call eval->Invalid()!
|
|||
else |
|||
return eval->Invalid("tokens cc inputs != cc outputs"); |
|||
} |
|||
} |
|||
|
|||
|
|||
switch (funcid) |
|||
{ |
|||
case 'c': // create wont be called to be verified as it has no CC inputs
|
|||
//vin.0: normal input
|
|||
//vout.0: issuance tokenoshis to CC
|
|||
//vout.1: normal output for change (if any)
|
|||
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
|
|||
//if (evalCodeInOpret != EVAL_TOKENS)
|
|||
// return eval->Invalid("unexpected TokenValidate for createtoken");
|
|||
//else
|
|||
return true; |
|||
|
|||
case 't': // transfer
|
|||
//vin.0: normal input
|
|||
//vin.1 .. vin.n-1: valid CC outputs
|
|||
//vout.0 to n-2: tokenoshis output to CC
|
|||
//vout.n-2: normal output for change (if any)
|
|||
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
|
|||
if (inputs == 0) |
|||
return eval->Invalid("no token inputs for transfer"); |
|||
|
|||
fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts); |
|||
break; // breaking to other contract validation...
|
|||
|
|||
default: |
|||
fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid); |
|||
return eval->Invalid("unexpected token funcid"); |
|||
} |
|||
|
|||
// forward validation if evalcode in opret is not EVAL_TOKENS
|
|||
// init for forwarding validation call
|
|||
//if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS?
|
|||
// struct CCcontract_info *cpOther = NULL, C;
|
|||
|
|||
// cpOther = CCinit(&C, evalCodeInOpret);
|
|||
// if (cpOther)
|
|||
// return cpOther->validate(cpOther, eval, tx, nIn);
|
|||
// else
|
|||
// return eval->Invalid("unsupported evalcode in opret");
|
|||
//}
|
|||
return true; |
|||
// what does this do?
|
|||
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
|
|||
} |
|||
|
|||
// helper funcs:
|
|||
|
|||
// extract my vins pubkeys:
|
|||
bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys) { |
|||
|
|||
bool found = false; |
|||
CPubKey pubkey; |
|||
struct CCcontract_info *cpTokens, tokensC; |
|||
|
|||
cpTokens = CCinit(&tokensC, EVAL_TOKENS); |
|||
|
|||
for (int32_t i = 0; i < tx.vin.size(); i++) |
|||
{ // check for additional contracts which may send tokens to the Tokens contract
|
|||
if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) |
|||
{ |
|||
|
|||
auto findEval = [](CC *cond, struct CCVisitor _) { |
|||
bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS;
|
|||
|
|||
if (cc_typeId(cond) == CC_Secp256k1) { |
|||
*(CPubKey*)_.context = buf2pk(cond->publicKey); |
|||
//std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl;
|
|||
r = true; |
|||
} |
|||
// false for a match, true for continue
|
|||
return r ? 0 : 1; |
|||
}; |
|||
|
|||
CC *cond = GetCryptoCondition(tx.vin[i].scriptSig); |
|||
|
|||
if (cond) { |
|||
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey }; |
|||
bool out = !cc_visit(cond, visitor); |
|||
cc_free(cond); |
|||
|
|||
if (pubkey.IsValid()) { |
|||
vinPubkeys.push_back(pubkey); |
|||
found = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return found; |
|||
} |
|||
|
|||
// this is just for log messages indentation fur debugging recursive calls:
|
|||
thread_local uint32_t tokenValIndentSize = 0; |
|||
|
|||
// validates opret for token tx:
|
|||
uint8_t ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) { |
|||
|
|||
uint256 tokenidOpret, tokenidOpret2; |
|||
uint8_t funcid; |
|||
uint8_t dummyEvalCode; |
|||
|
|||
// this is just for log messages indentation fur debugging recursive calls:
|
|||
std::string indentStr = std::string().append(tokenValIndentSize, '.'); |
|||
|
|||
int32_t n = tx.vout.size(); |
|||
|
|||
if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) |
|||
{ |
|||
std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl; |
|||
return(false); |
|||
} |
|||
else if (funcid == 'c') |
|||
{ |
|||
if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) { |
|||
//std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
|
|||
return funcid; |
|||
} |
|||
} |
|||
else if (funcid == 't') |
|||
{ |
|||
//std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
|
|||
if (tokenid != zeroid && tokenid == tokenidOpret) { |
|||
//std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
|
|||
return funcid; |
|||
} |
|||
} |
|||
//std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
|
|||
return (uint8_t)0; |
|||
} |
|||
|
|||
// Checks if the vout is a really Tokens CC vout
|
|||
// also checks tokenid in opret or txid if this is 'c' tx
|
|||
// goDeeper is true: the func also validates amounts of the passed transaction:
|
|||
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
|
|||
// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true!
|
|||
int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) |
|||
{ |
|||
|
|||
// this is just for log messages indentation fur debugging recursive calls:
|
|||
std::string indentStr = std::string().append(tokenValIndentSize, '.'); |
|||
//std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
|
|||
//TODO: validate cc vouts are EVAL_TOKENS!
|
|||
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
|
|||
{ |
|||
int32_t n = tx.vout.size(); |
|||
// just check boundaries:
|
|||
if (v >= n - 1) { // just moved this up (dimxy)
|
|||
std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl; |
|||
return(0); |
|||
} |
|||
|
|||
if (goDeeper) { |
|||
//std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl;
|
|||
//validate all tx
|
|||
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; |
|||
|
|||
tokenValIndentSize++; |
|||
// false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels
|
|||
bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid); |
|||
tokenValIndentSize--; |
|||
|
|||
if (!isEqual) { |
|||
// if ccInputs != ccOutputs and it is not the tokenbase tx
|
|||
// this means it is possibly a fake tx (dimxy):
|
|||
if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
|
|||
std::cerr << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl; |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// moved opret checking to this new reusable func (dimxy):
|
|||
std::vector<CPubKey> voutPubkeys; |
|||
std::vector<uint8_t> vopretExtra; |
|||
const uint8_t funcId = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra); |
|||
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
if (funcId != 0) { |
|||
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
|
|||
if (checkPubkeys && funcId != 'c') { // verify that the vout is token's (for 'c' there is no pubkeys!):
|
|||
|
|||
//std::cerr << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl;
|
|||
|
|||
uint8_t evalCodeInOpret; |
|||
if (vopretExtra.size() >= 2 /*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/) { |
|||
evalCodeInOpret = vopretExtra.begin()[1]; |
|||
} |
|||
else { |
|||
// if payload is empty maybe it is a claim to non-payload-one-token-eval vout?
|
|||
evalCodeInOpret = EVAL_TOKENS; |
|||
} |
|||
|
|||
// maybe this is dual-eval 1 pubkey or 1of2 pubkey vout?
|
|||
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { |
|||
CTxOut testDualVout; |
|||
// check dual-eval 1 pubkey vout with the first pubkey
|
|||
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0]); |
|||
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=0), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
|
|||
if(voutPubkeys.size() == 2) { |
|||
// check dual eval 1of2 pubkeys vout
|
|||
testDualVout = MakeTokensCC1of2vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]); |
|||
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token 1of2 vout, eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
|
|||
// check dual eval 1 pubkey vout with the second pubkey
|
|||
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[1]); |
|||
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=1), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
} |
|||
|
|||
|
|||
// maybe this is claim to single-eval token?
|
|||
CTxOut testTokenVout1; |
|||
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]); |
|||
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=0), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
|
|||
if (voutPubkeys.size() == 2) { |
|||
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]); |
|||
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=1), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// maybe it is single-eval or dual-eval token change?
|
|||
std::vector<CPubKey> vinPubkeys; |
|||
ExtractTokensVinPubkeys(tx, vinPubkeys); |
|||
|
|||
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { |
|||
CTxOut testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it); |
|||
CTxOut testDualVout1 = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, *it); |
|||
|
|||
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is single-eval token change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
|
|||
if (tx.vout[v].scriptPubKey == testDualVout1.scriptPubKey) { |
|||
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token change, vout eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
//std::cerr << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
|
|||
return tx.vout[v].nValue; |
|||
} |
|||
} |
|||
|
|||
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
|
|||
} |
|||
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
|
|||
return(0); |
|||
} |
|||
|
|||
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
|
|||
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) |
|||
{ |
|||
CTransaction vinTx; |
|||
uint256 hashBlock; |
|||
int64_t tokenoshis; |
|||
|
|||
struct CCcontract_info *cpTokens, tokensC; |
|||
cpTokens = CCinit(&tokensC, EVAL_TOKENS); |
|||
|
|||
int32_t numvins = tx.vin.size(); |
|||
int32_t numvouts = tx.vout.size(); |
|||
inputs = outputs = 0; |
|||
|
|||
// this is just for log messages indentation for debugging recursive calls:
|
|||
std::string indentStr = std::string().append(tokenValIndentSize, '.'); |
|||
|
|||
for (int32_t i = 0; i<numvins; i++) |
|||
{ // check for additional contracts which may send tokens to the Tokens contract
|
|||
if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) |
|||
{ |
|||
//std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
|
|||
// we are not inside the validation code -- dimxy
|
|||
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) |
|||
{ |
|||
std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; |
|||
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); |
|||
} |
|||
else { |
|||
tokenValIndentSize++; |
|||
// validate vouts of vintx
|
|||
//std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
|
|||
tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, tokenid); |
|||
tokenValIndentSize--; |
|||
if (tokenoshis != 0) |
|||
{ |
|||
std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl; |
|||
inputs += tokenoshis; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
for (int32_t i = 0; i<numvouts; i++) |
|||
{ |
|||
tokenValIndentSize++; |
|||
// Note: we pass in here 'false' because we don't need to call TokenExactAmounts() recursively from IsTokensvout
|
|||
// indeed, in this case we'll be checking this tx again
|
|||
//std::cerr << indentStr << "TokenExactAmounts() check vout i=" << i << " nValue=" << tx.vout[i].nValue << std::endl;
|
|||
tokenoshis = IsTokensvout(false, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, tokenid); |
|||
tokenValIndentSize--; |
|||
|
|||
if (tokenoshis != 0) |
|||
{ |
|||
std::cerr << indentStr << "TokensExactAmounts() vout i=" << i << " tokenoshis=" << tokenoshis << std::endl; |
|||
outputs += tokenoshis; |
|||
} |
|||
} |
|||
|
|||
//std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
|
|||
|
|||
if (inputs != outputs) { |
|||
if (tx.GetHash() != tokenid) |
|||
std::cerr << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl; |
|||
return false; // do not call eval->Invalid() here!
|
|||
} |
|||
else |
|||
return true; |
|||
} |
|||
|
|||
// add inputs from token cc addr
|
|||
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) |
|||
{ |
|||
char tokenaddr[64], destaddr[64]; |
|||
int64_t threshold, nValue, price, totalinputs = 0; |
|||
uint256 txid, hashBlock; |
|||
//std::vector<uint8_t> vopretExtra;
|
|||
CTransaction vintx; |
|||
int32_t j, vout, n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
|
|||
GetTokensCCaddress(cp, tokenaddr, pk); |
|||
SetCCunspents(unspentOutputs, tokenaddr); |
|||
|
|||
threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: is maxinputs really could not be over 64? what if i want to calc total balance?
|
|||
|
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
if (it->second.satoshis < threshold) |
|||
continue; |
|||
for (j = 0; j<mtx.vin.size(); j++) |
|||
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n) |
|||
break; |
|||
if (j != mtx.vin.size()) |
|||
continue; |
|||
|
|||
if (GetTransaction(txid, vintx, hashBlock, false) != 0) |
|||
{ |
|||
Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); |
|||
if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0) |
|||
continue; |
|||
//fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN);
|
|||
|
|||
std::vector<CPubKey> vinPubkeys; |
|||
|
|||
if ((nValue = IsTokensvout(true, true/*<--add only checked token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) |
|||
{ |
|||
if (total != 0 && maxinputs != 0) |
|||
mtx.vin.push_back(CTxIn(txid, vout, CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl; |
|||
n++; |
|||
if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
//std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl;
|
|||
return(totalinputs); |
|||
} |
|||
|
|||
|
|||
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description) |
|||
{ |
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); |
|||
CPubKey mypk; struct CCcontract_info *cp, C; |
|||
if (assetsupply < 0) |
|||
{ |
|||
fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply); |
|||
return(""); |
|||
} |
|||
|
|||
cp = CCinit(&C, EVAL_TOKENS); |
|||
if (name.size() > 32 || description.size() > 4096) |
|||
{ |
|||
fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size()); |
|||
return(""); |
|||
} |
|||
if (txfee == 0) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
|
|||
if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk)); |
|||
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); |
|||
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description))); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
|
|||
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total) |
|||
{ |
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); |
|||
CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; |
|||
std::vector<uint8_t> emptyExtraOpret; |
|||
|
|||
if (total < 0) |
|||
{ |
|||
fprintf(stderr, "negative total %lld\n", (long long)total); |
|||
return(""); |
|||
} |
|||
cp = CCinit(&C, EVAL_TOKENS); |
|||
if (txfee == 0) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) |
|||
{ |
|||
//n = outputs.size();
|
|||
//if ( n == amounts.size() )
|
|||
//{
|
|||
// for (i=0; i<n; i++)
|
|||
// total += amounts[i];
|
|||
mask = ~((1LL << mtx.vin.size()) - 1); |
|||
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, total, 60)) > 0) |
|||
{ |
|||
|
|||
if (inputs < total) { //added dimxy
|
|||
std::cerr << "AssetTransfer(): insufficient funds" << std::endl; |
|||
return (""); |
|||
} |
|||
if (inputs > total) |
|||
CCchange = (inputs - total); |
|||
//for (i=0; i<n; i++)
|
|||
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, total, pubkey2pk(destpubkey))); // TODO: or MakeTokensCC1vout??
|
|||
if (CCchange != 0) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); |
|||
|
|||
std::vector<CPubKey> voutTokenPubkeys; |
|||
voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout
|
|||
|
|||
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet('t', EVAL_TOKENS, assetid, voutTokenPubkeys, CScript()))); // By setting EVAL_TOKENS we're getting out from assets validation code
|
|||
} |
|||
else { |
|||
fprintf(stderr, "not enough CC token inputs for %.8f\n", (double)total / COIN); |
|||
} |
|||
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
|
|||
} |
|||
else { |
|||
fprintf(stderr, "not enough normal inputs for txfee\n"); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
|
|||
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) |
|||
{ |
|||
uint256 hashBlock; |
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); |
|||
CTransaction tokentx; |
|||
|
|||
// CCerror = strprintf("obsolete, cannot return correct value without eval");
|
|||
// return 0;
|
|||
|
|||
if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0) |
|||
{ |
|||
fprintf(stderr, "cant find tokenid\n"); |
|||
CCerror = strprintf("cant find tokenid"); |
|||
return 0; |
|||
} |
|||
|
|||
struct CCcontract_info *cp, C; |
|||
cp = CCinit(&C, EVAL_TOKENS); |
|||
return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0)); |
|||
} |
|||
|
|||
UniValue TokenInfo(uint256 tokenid) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name, description; char str[67], numstr[65]; |
|||
if (GetTransaction(tokenid, vintx, hashBlock, false) == 0) |
|||
{ |
|||
fprintf(stderr, "cant find assetid\n"); |
|||
result.push_back(Pair("result", "error")); |
|||
result.push_back(Pair("error", "cant find tokenid")); |
|||
return(result); |
|||
} |
|||
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) == 0) |
|||
{ |
|||
fprintf(stderr, "assetid isnt token creation txid\n"); |
|||
result.push_back(Pair("result", "error")); |
|||
result.push_back(Pair("error", "assetid isnt token creation txid")); |
|||
} |
|||
result.push_back(Pair("result", "success")); |
|||
result.push_back(Pair("tokenid", uint256_str(str, tokenid))); |
|||
result.push_back(Pair("owner", pubkey33_str(str, origpubkey.data()))); |
|||
result.push_back(Pair("name", name)); |
|||
result.push_back(Pair("supply", vintx.vout[0].nValue)); |
|||
result.push_back(Pair("description", description)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue TokenList() |
|||
{ |
|||
UniValue result(UniValue::VARR); |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; |
|||
struct CCcontract_info *cp, C; uint256 txid, hashBlock; |
|||
CTransaction vintx; std::vector<uint8_t> origpubkey; |
|||
std::string name, description; char str[65]; |
|||
|
|||
cp = CCinit(&C, EVAL_TOKENS); |
|||
SetCCtxids(addressIndex, cp->normaladdr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
if (GetTransaction(txid, vintx, hashBlock, false) != 0) |
|||
{ |
|||
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) |
|||
{ |
|||
result.push_back(uint256_str(str, txid)); |
|||
} |
|||
} |
|||
} |
|||
return(result); |
|||
} |
@ -0,0 +1,44 @@ |
|||
/******************************************************************************
|
|||
* Copyright © 2014-2018 The SuperNET Developers. * |
|||
* * |
|||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * |
|||
* the top-level directory of this distribution for the individual copyright * |
|||
* holder information and the developer policies on copyright and licensing. * |
|||
* * |
|||
* Unless otherwise agreed in a custom licensing agreement, no part of the * |
|||
* SuperNET software, including this file may be copied, modified, propagated * |
|||
* or distributed except according to the terms contained in the LICENSE file * |
|||
* * |
|||
* Removal or modification of this copyright notice is prohibited. * |
|||
* * |
|||
******************************************************************************/ |
|||
|
|||
|
|||
/*
|
|||
CCassetstx has the functions that create the EVAL_ASSETS transactions. It is expected that rpc calls would call these functions. For EVAL_ASSETS, the rpc functions are in rpcwallet.cpp |
|||
|
|||
CCassetsCore has functions that are used in two contexts, both during rpc transaction create time and also during the blockchain validation. Using the identical functions is a good way to prevent them from being mismatched. The must match or the transaction will get rejected. |
|||
*/ |
|||
|
|||
#ifndef CC_TOKENS_H |
|||
#define CC_TOKENS_H |
|||
|
|||
#include "CCinclude.h" |
|||
|
|||
// CCcustom
|
|||
bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); |
|||
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); |
|||
//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector<CPubKey> vinPubkeys);
|
|||
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description); |
|||
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total); |
|||
|
|||
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); |
|||
UniValue TokenInfo(uint256 tokenid); |
|||
UniValue TokenList(); |
|||
|
|||
//this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
|
|||
|
|||
//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
|
|||
|
|||
|
|||
#endif |
@ -0,0 +1,317 @@ |
|||
/******************************************************************************
|
|||
* 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 <assert.h> |
|||
#include <cryptoconditions.h> |
|||
|
|||
#include "primitives/block.h" |
|||
#include "primitives/transaction.h" |
|||
#include "script/cc.h" |
|||
#include "cc/eval.h" |
|||
#include "cc/utils.h" |
|||
#include "cc/CCinclude.h" |
|||
#include "main.h" |
|||
#include "chain.h" |
|||
#include "core_io.h" |
|||
#include "crosschain.h" |
|||
|
|||
#define FAUCET2SIZE COIN |
|||
|
|||
struct CClib_rpcinfo |
|||
{ |
|||
char *method,*help; |
|||
int32_t numrequiredargs,maxargs; // frontloaded with required
|
|||
uint8_t funcid; |
|||
} |
|||
CClib_methods[] = |
|||
{ |
|||
{ (char *)"faucet2_fund", (char *)"amount", 1, 1, 'F' }, |
|||
{ (char *)"faucet2_get", (char *)"<no args>", 0, 0, 'G' }, |
|||
}; |
|||
|
|||
std::string MYCCLIBNAME = (char *)"faucet2"; |
|||
|
|||
char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } |
|||
|
|||
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params); |
|||
|
|||
UniValue CClib_info(struct CCcontract_info *cp) |
|||
{ |
|||
UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i; char str[2]; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("CClib",CClib_name())); |
|||
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++) |
|||
{ |
|||
UniValue obj(UniValue::VOBJ); |
|||
if ( CClib_methods[i].funcid < ' ' || CClib_methods[i].funcid >= 128 ) |
|||
obj.push_back(Pair("funcid",CClib_methods[i].funcid)); |
|||
else |
|||
{ |
|||
str[0] = CClib_methods[i].funcid; |
|||
str[1] = 0; |
|||
obj.push_back(Pair("funcid",str)); |
|||
} |
|||
obj.push_back(Pair("name",CClib_methods[i].method)); |
|||
obj.push_back(Pair("help",CClib_methods[i].help)); |
|||
obj.push_back(Pair("params_required",CClib_methods[i].numrequiredargs)); |
|||
obj.push_back(Pair("params_max",CClib_methods[i].maxargs)); |
|||
a.push_back(obj); |
|||
} |
|||
result.push_back(Pair("methods",a)); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; |
|||
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++) |
|||
{ |
|||
if ( strcmp(method,CClib_methods[i].method) == 0 ) |
|||
{ |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("method",CClib_methods[i].method)); |
|||
rawtx = CClib_rawtxgen(cp,CClib_methods[i].funcid,params); |
|||
result.push_back(Pair("rawtx",rawtx)); |
|||
return(result); |
|||
} |
|||
} |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("method",CClib_methods[i].method)); |
|||
result.push_back(Pair("error","method not found")); |
|||
return(result); |
|||
} |
|||
|
|||
int64_t IsCClibvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) |
|||
{ |
|||
char destaddr[64]; |
|||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) |
|||
{ |
|||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) |
|||
return(tx.vout[v].nValue); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) |
|||
{ |
|||
static uint256 zerohash; |
|||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
//fprintf(stderr,"vini.%d\n",i);
|
|||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) |
|||
{ |
|||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
|||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) |
|||
return eval->Invalid("cant find vinTx"); |
|||
else |
|||
{ |
|||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
|||
if ( hashBlock == zerohash ) |
|||
return eval->Invalid("cant faucet2 from mempool"); |
|||
if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) |
|||
inputs += assetoshis; |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<numvouts; i++) |
|||
{ |
|||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
|||
if ( (assetoshis= IsCClibvout(cp,tx,i)) != 0 ) |
|||
outputs += assetoshis; |
|||
} |
|||
if ( inputs != outputs+FAUCET2SIZE+txfee ) |
|||
{ |
|||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); |
|||
return eval->Invalid("mismatched inputs != outputs + FAUCET2SIZE + txfee"); |
|||
} |
|||
else return(true); |
|||
} |
|||
|
|||
bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn) |
|||
{ |
|||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; |
|||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids; |
|||
numvins = tx.vin.size(); |
|||
numvouts = tx.vout.size(); |
|||
preventCCvins = preventCCvouts = -1; |
|||
if ( numvouts < 1 ) |
|||
return eval->Invalid("no vouts"); |
|||
else |
|||
{ |
|||
for (i=0; i<numvins; i++) |
|||
{ |
|||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) |
|||
{ |
|||
fprintf(stderr,"faucetget invalid vini\n"); |
|||
return eval->Invalid("illegal normal vini"); |
|||
} |
|||
} |
|||
//fprintf(stderr,"check amounts\n");
|
|||
if ( CClibExactAmounts(cp,eval,tx,1,10000) == false ) |
|||
{ |
|||
fprintf(stderr,"faucetget invalid amount\n"); |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
preventCCvouts = 1; |
|||
if ( IsCClibvout(cp,tx,0) != 0 ) |
|||
{ |
|||
preventCCvouts++; |
|||
i = 1; |
|||
} else i = 0; |
|||
txid = tx.GetHash(); |
|||
memcpy(hash,&txid,sizeof(hash)); |
|||
fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]); |
|||
if ( tx.vout[i].nValue != FAUCET2SIZE ) |
|||
return eval->Invalid("invalid faucet output"); |
|||
else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) |
|||
return eval->Invalid("invalid faucetget txid"); |
|||
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); |
|||
SetCCtxids(txids,destaddr); |
|||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++) |
|||
{ |
|||
//int height = it->first.blockHeight;
|
|||
if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) |
|||
{ |
|||
//fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks);
|
|||
return eval->Invalid("faucet2 is only for brand new addresses"); |
|||
} |
|||
} |
|||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); |
|||
if ( retval != 0 ) |
|||
fprintf(stderr,"faucet2get validated\n"); |
|||
else fprintf(stderr,"faucet2get invalid\n"); |
|||
return(retval); |
|||
} |
|||
} |
|||
} |
|||
|
|||
int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) |
|||
{ |
|||
char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0; |
|||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; |
|||
GetCCaddress(cp,coinaddr,pk); |
|||
SetCCunspents(unspentOutputs,coinaddr); |
|||
threshold = total/(maxinputs+1); |
|||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) |
|||
{ |
|||
txid = it->first.txhash; |
|||
vout = (int32_t)it->first.index; |
|||
if ( it->second.satoshis < threshold ) |
|||
continue; |
|||
//char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
|
|||
// no need to prevent dup
|
|||
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) |
|||
{ |
|||
if ( (nValue= IsCClibvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) |
|||
{ |
|||
if ( total != 0 && maxinputs != 0 ) |
|||
mtx.vin.push_back(CTxIn(txid,vout,CScript())); |
|||
nValue = it->second.satoshis; |
|||
totalinputs += nValue; |
|||
n++; |
|||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) |
|||
break; |
|||
} else fprintf(stderr,"nValue too small or already spent in mempool\n"); |
|||
} else fprintf(stderr,"couldnt get tx\n"); |
|||
} |
|||
return(totalinputs); |
|||
} |
|||
|
|||
|
|||
std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) |
|||
{ |
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); |
|||
CPubKey mypk,cclibpk; CScript opret; |
|||
if ( txfee == 0 ) |
|||
txfee = 10000; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
cclibpk = GetUnspendable(cp,0); |
|||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) |
|||
{ |
|||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk)); |
|||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); |
|||
} |
|||
return(""); |
|||
} |
|||
|
|||
/*UniValue FaucetInfo()
|
|||
{ |
|||
UniValue result(UniValue::VOBJ); char numstr[64]; |
|||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); |
|||
CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding; |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("name","Faucet")); |
|||
cp = CCinit(&C,EVAL_FAUCET); |
|||
faucetpk = GetUnspendable(cp,0); |
|||
funding = AddFaucetInputs(cp,mtx,faucetpk,0,0); |
|||
sprintf(numstr,"%.8f",(double)funding/COIN); |
|||
result.push_back(Pair("funding",numstr)); |
|||
return(result); |
|||
}*/ |
|||
|
|||
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params) |
|||
{ |
|||
CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_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)) > 0 ) |
|||
{ |
|||
if ( inputs > nValue ) |
|||
CCchange = (inputs - nValue - txfee); |
|||
if ( CCchange != 0 ) |
|||
mtx.vout.push_back(MakeCC1vout(EVAL_FIRSTUSER,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_FIRSTUSER << (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(""); |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,667 @@ |
|||
#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
|
|||
CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName); |
|||
CScript 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, 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) { |
|||
return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); |
|||
} |
|||
static CScript makeAddOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { |
|||
return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); |
|||
} |
|||
static CScript makeClaimOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { |
|||
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) { |
|||
CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); |
|||
} |
|||
}; |
|||
|
|||
// 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); |
|||
} |
|||
|
|||
static CScript makeCreateOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { |
|||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys, |
|||
EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); |
|||
} |
|||
static CScript makeAddOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { |
|||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys, |
|||
EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); |
|||
} |
|||
static CScript makeClaimOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { |
|||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys, |
|||
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) { |
|||
|
|||
CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); |
|||
} |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* 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; |
|||
uint256 tokenid; |
|||
|
|||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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; |
|||
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, 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; |
|||
uint256 tokenid; |
|||
|
|||
// get heir pubkey:
|
|||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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 |
@ -0,0 +1 @@ |
|||
gcc -std=c++11 -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../cclib.so cclib.cpp |
File diff suppressed because it is too large
Loading…
Reference in new issue