Hush Full Node software. We were censored from Github, this is where all development happens now.
https://hush.is
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
900 lines
32 KiB
900 lines
32 KiB
// Copyright (c) 2016-2021 The Hush developers
|
|
// Distributed under the GPLv3 software license, see the accompanying
|
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
/******************************************************************************
|
|
* Copyright © 2014-2019 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
// CCutils has low level functions that are universally useful for all contracts.
|
|
#include "CCinclude.h"
|
|
#include "hush_structs.h"
|
|
#include "key_io.h"
|
|
|
|
#ifdef TESTMODE
|
|
#define MIN_NON_NOTARIZED_CONFIRMS 2
|
|
#else
|
|
#define MIN_NON_NOTARIZED_CONFIRMS 101
|
|
#endif // TESTMODE
|
|
int32_t hush_dpowconfs(int32_t height,int32_t numconfs);
|
|
struct hush_state *hush_stateptr(char *symbol,char *dest);
|
|
extern uint32_t HUSH_DPOWCONFS;
|
|
|
|
void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
|
|
{
|
|
int32_t i,j=0;
|
|
#if defined(WORDS_BIGENDIAN)
|
|
for (i=31; i>=0; i--)
|
|
dest[j++] = src[i];
|
|
#else
|
|
memcpy(dest,src,len);
|
|
#endif
|
|
}
|
|
|
|
CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2)
|
|
{
|
|
std::vector<CC*> pks;
|
|
pks.push_back(CCNewSecp256k1(pk1));
|
|
pks.push_back(CCNewSecp256k1(pk2));
|
|
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
|
|
CC *Sig = CCNewThreshold(1, pks);
|
|
return CCNewThreshold(2, {condCC, Sig});
|
|
}
|
|
|
|
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk)
|
|
{
|
|
std::vector<CC*> pks;
|
|
pks.push_back(CCNewSecp256k1(pk));
|
|
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
|
|
CC *Sig = CCNewThreshold(1, pks);
|
|
return CCNewThreshold(2, {condCC, Sig});
|
|
}
|
|
|
|
int32_t has_opret(const CTransaction &tx, uint8_t evalcode)
|
|
{
|
|
int i = 0;
|
|
for ( auto vout : tx.vout )
|
|
{
|
|
//fprintf(stderr, "[txid.%s] 1.%i 2.%i 3.%i 4.%i\n",tx.GetHash().GetHex().c_str(), vout.scriptPubKey[0], vout.scriptPubKey[1], vout.scriptPubKey[2], vout.scriptPubKey[3]);
|
|
if ( vout.scriptPubKey.size() > 3 && vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[2] == evalcode )
|
|
return i;
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool getCCopret(const CScript &scriptPubKey, CScript &opret)
|
|
{
|
|
std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
|
|
CScript dummy; bool ret = false;
|
|
if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0 )
|
|
{
|
|
ret = true;
|
|
if ( vParams.size() == 1)
|
|
{
|
|
opret = CScript(vParams[0].begin()+6, vParams[0].end());
|
|
//fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool makeCCopret(CScript &opret, std::vector<std::vector<unsigned char>> &vData)
|
|
{
|
|
if ( opret.empty() )
|
|
return false;
|
|
vData.push_back(std::vector<unsigned char>(opret.begin(), opret.end()));
|
|
return true;
|
|
}
|
|
|
|
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector<std::vector<unsigned char>>* vData)
|
|
{
|
|
CTxOut vout;
|
|
CC *payoutCond = MakeCCcond1(evalcode,pk);
|
|
vout = CTxOut(nValue,CCPubKey(payoutCond));
|
|
if ( vData )
|
|
{
|
|
//std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
|
|
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
|
|
//vPubKeys.push_back(pk);
|
|
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, ( * vData));
|
|
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
|
|
}
|
|
cc_free(payoutCond);
|
|
return(vout);
|
|
}
|
|
|
|
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, std::vector<std::vector<unsigned char>>* vData)
|
|
{
|
|
CTxOut vout;
|
|
CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2);
|
|
vout = CTxOut(nValue,CCPubKey(payoutCond));
|
|
if ( vData )
|
|
{
|
|
//std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
|
|
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
|
|
// skip pubkeys. These need to maybe be optional and we need some way to get them out that is easy!
|
|
//vPubKeys.push_back(pk1);
|
|
//vPubKeys.push_back(pk2);
|
|
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, ( * vData));
|
|
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
|
|
}
|
|
cc_free(payoutCond);
|
|
return(vout);
|
|
}
|
|
|
|
CC* GetCryptoCondition(CScript const& scriptSig)
|
|
{
|
|
auto pc = scriptSig.begin();
|
|
opcodetype opcode;
|
|
std::vector<unsigned char> ffbin;
|
|
if (scriptSig.GetOp(pc, opcode, ffbin))
|
|
return cc_readFulfillmentBinary((uint8_t*)ffbin.data(), ffbin.size()-1);
|
|
else return(0);
|
|
}
|
|
|
|
bool IsCCInput(CScript const& scriptSig)
|
|
{
|
|
CC *cond;
|
|
if ( (cond= GetCryptoCondition(scriptSig)) == 0 )
|
|
return false;
|
|
cc_free(cond);
|
|
return true;
|
|
}
|
|
|
|
bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime, int64_t &actualtxfee)
|
|
{
|
|
LOCK(mempool.cs);
|
|
CCoinsView dummy;
|
|
CCoinsViewCache view(&dummy);
|
|
int64_t interest; uint64_t valuein;
|
|
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
|
|
view.SetBackend(viewMemPool);
|
|
valuein = view.GetValueIn(height,&interest,tx,blocktime);
|
|
actualtxfee = valuein-tx.GetValueOut();
|
|
if ( actualtxfee > txfee )
|
|
{
|
|
//fprintf(stderr, "actualtxfee.%li vs txfee.%li\n", actualtxfee, txfee);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint32_t GetLatestTimestamp(int32_t height)
|
|
{
|
|
if ( HUSH_NSPV_SUPERLITE ) return ((uint32_t)NSPV_blocktime(height));
|
|
return(hush_heightstamp(height));
|
|
} // :P
|
|
|
|
void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr)
|
|
{
|
|
cp->unspendableEvalcode2 = evalcode;
|
|
cp->unspendablepk2 = pk;
|
|
memcpy(cp->unspendablepriv2,priv,32);
|
|
strcpy(cp->unspendableaddr2,coinaddr);
|
|
}
|
|
|
|
void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr)
|
|
{
|
|
cp->unspendableEvalcode3 = evalcode;
|
|
cp->unspendablepk3 = pk;
|
|
memcpy(cp->unspendablepriv3,priv,32);
|
|
strcpy(cp->unspendableaddr3,coinaddr);
|
|
}
|
|
|
|
void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *coinaddr)
|
|
{
|
|
cp->coins1of2pk[0] = pk1;
|
|
cp->coins1of2pk[1] = pk2;
|
|
memcpy(cp->coins1of2priv,priv,32);
|
|
strcpy(cp->coins1of2addr,coinaddr);
|
|
}
|
|
|
|
void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, uint8_t *priv, char *tokenaddr)
|
|
{
|
|
cp->tokens1of2pk[0] = pk1;
|
|
cp->tokens1of2pk[1] = pk2;
|
|
memcpy(cp->tokens1of2priv,priv,32);
|
|
strcpy(cp->tokens1of2addr, tokenaddr);
|
|
}
|
|
|
|
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
|
|
{
|
|
CTxDestination address; txnouttype whichType;
|
|
destaddr[0] = 0;
|
|
if ( scriptPubKey.begin() != 0 )
|
|
{
|
|
if ( ExtractDestination(scriptPubKey,address) != 0 )
|
|
{
|
|
strcpy(destaddr,(char *)CBitcoinAddress(address).ToString().c_str());
|
|
return(true);
|
|
}
|
|
}
|
|
//fprintf(stderr,"ExtractDestination failed\n");
|
|
return(false);
|
|
}
|
|
|
|
bool GetCustomscriptaddress(char *destaddr,const CScript &scriptPubKey,uint8_t taddr,uint8_t prefix, uint8_t prefix2)
|
|
{
|
|
CTxDestination address; txnouttype whichType;
|
|
if ( ExtractDestination(scriptPubKey,address) != 0 )
|
|
{
|
|
strcpy(destaddr,(char *)CCustomBitcoinAddress(address,taddr,prefix,prefix2).ToString().c_str());
|
|
return(true);
|
|
}
|
|
//fprintf(stderr,"ExtractDestination failed\n");
|
|
return(false);
|
|
}
|
|
|
|
bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn,
|
|
CTransaction &txOut, std::vector<std::vector<unsigned char>> &preConditions, std::vector<std::vector<unsigned char>> ¶ms)
|
|
{
|
|
uint256 blockHash;
|
|
|
|
if (myGetTransaction(tx.vin[nIn].prevout.hash, txOut, blockHash) && txOut.vout.size() > tx.vin[nIn].prevout.n)
|
|
{
|
|
CBlockIndex index;
|
|
if (eval->GetBlock(blockHash, index))
|
|
{
|
|
// read preconditions
|
|
CScript subScript = CScript();
|
|
preConditions.clear();
|
|
if (txOut.vout[tx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(&subScript, preConditions))
|
|
{
|
|
// read any available parameters in the output transaction
|
|
params.clear();
|
|
if (tx.vout.size() > 0 && tx.vout[tx.vout.size() - 1].scriptPubKey.IsOpReturn())
|
|
{
|
|
if (tx.vout[tx.vout.size() - 1].scriptPubKey.GetOpretData(params) && params.size() == 1)
|
|
{
|
|
CScript scr = CScript(params[0].begin(), params[0].end());
|
|
|
|
// printf("Script decoding inner:\n%s\nouter:\n%s\n", scr.ToString().c_str(), tx.vout[tx.vout.size() - 1].scriptPubKey.ToString().c_str());
|
|
|
|
if (!scr.GetPushedData(scr.begin(), params))
|
|
{
|
|
return false;
|
|
}
|
|
else return true;
|
|
}
|
|
else return false;
|
|
}
|
|
else return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
//fprintf(stderr,"ExtractDestination failed\n");
|
|
return(false);
|
|
}
|
|
|
|
bool pubkey2addr(char *destaddr,uint8_t *pubkey33)
|
|
{
|
|
std::vector<uint8_t>pk; int32_t i;
|
|
for (i=0; i<33; i++)
|
|
pk.push_back(pubkey33[i]);
|
|
return(Getscriptaddress(destaddr,CScript() << pk << OP_CHECKSIG));
|
|
}
|
|
|
|
CPubKey CCtxidaddr(char *txidaddr,uint256 txid)
|
|
{
|
|
uint8_t buf33[33]; CPubKey pk;
|
|
buf33[0] = 0x02;
|
|
endiancpy(&buf33[1],(uint8_t *)&txid,32);
|
|
pk = buf2pk(buf33);
|
|
Getscriptaddress(txidaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG);
|
|
return(pk);
|
|
}
|
|
|
|
CPubKey CCCustomtxidaddr(char *txidaddr,uint256 txid,uint8_t taddr,uint8_t prefix,uint8_t prefix2)
|
|
{
|
|
uint8_t buf33[33]; CPubKey pk;
|
|
buf33[0] = 0x02;
|
|
endiancpy(&buf33[1],(uint8_t *)&txid,32);
|
|
pk = buf2pk(buf33);
|
|
GetCustomscriptaddress(txidaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG,taddr,prefix,prefix2);
|
|
return(pk);
|
|
}
|
|
|
|
bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk)
|
|
{
|
|
CC *payoutCond;
|
|
destaddr[0] = 0;
|
|
if ( (payoutCond= MakeCCcond1(evalcode,pk)) != 0 )
|
|
{
|
|
Getscriptaddress(destaddr,CCPubKey(payoutCond));
|
|
cc_free(payoutCond);
|
|
}
|
|
return(destaddr[0] != 0);
|
|
}
|
|
|
|
bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk)
|
|
{
|
|
destaddr[0] = 0;
|
|
if ( pk.size() == 0 )
|
|
pk = GetUnspendable(cp,0);
|
|
return(_GetCCaddress(destaddr,cp->evalcode,pk));
|
|
}
|
|
|
|
bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
|
|
{
|
|
CC *payoutCond;
|
|
destaddr[0] = 0;
|
|
if ((payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk)) != 0)
|
|
{
|
|
Getscriptaddress(destaddr, CCPubKey(payoutCond));
|
|
cc_free(payoutCond);
|
|
}
|
|
return(destaddr[0] != 0);
|
|
}
|
|
|
|
// get scriptPubKey adddress for three/dual eval token cc vout
|
|
bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk)
|
|
{
|
|
destaddr[0] = 0;
|
|
if (pk.size() == 0)
|
|
pk = GetUnspendable(cp, 0);
|
|
return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->additionalTokensEvalcode2, pk));
|
|
}
|
|
|
|
|
|
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2)
|
|
{
|
|
CC *payoutCond;
|
|
destaddr[0] = 0;
|
|
if ( (payoutCond= MakeCCcond1of2(cp->evalcode,pk,pk2)) != 0 )
|
|
{
|
|
Getscriptaddress(destaddr,CCPubKey(payoutCond));
|
|
cc_free(payoutCond);
|
|
}
|
|
return(destaddr[0] != 0);
|
|
}
|
|
|
|
bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2)
|
|
{
|
|
CC *payoutCond;
|
|
destaddr[0] = 0;
|
|
if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc
|
|
{
|
|
Getscriptaddress(destaddr, CCPubKey(payoutCond));
|
|
cc_free(payoutCond);
|
|
}
|
|
return(destaddr[0] != 0);
|
|
}
|
|
|
|
bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue)
|
|
{
|
|
char destaddr[64];
|
|
if ( vout.scriptPubKey.IsPayToCryptoCondition() != CCflag )
|
|
{
|
|
fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n", vout.scriptPubKey.IsPayToCryptoCondition(), CCflag);
|
|
return(false);
|
|
}
|
|
else if ( cmpaddr != 0 && (Getscriptaddress(destaddr, vout.scriptPubKey) == 0 || strcmp(destaddr, cmpaddr) != 0) )
|
|
{
|
|
fprintf(stderr,"constrain vout error: check addr %s vs script addr %s\n", cmpaddr!=0?cmpaddr:"", destaddr!=0?destaddr:"");
|
|
return(false);
|
|
}
|
|
else if ( nValue != 0 && nValue != vout.nValue ) //(nValue == 0 && vout.nValue < 10000) || (
|
|
{
|
|
fprintf(stderr,"constrain vout error nValue %.8f vs %.8f\n",(double)nValue/COIN,(double)vout.nValue/COIN);
|
|
return(false);
|
|
}
|
|
else return(true);
|
|
}
|
|
|
|
bool PreventCC(Eval* eval,const CTransaction &tx,int32_t preventCCvins,int32_t numvins,int32_t preventCCvouts,int32_t numvouts)
|
|
{
|
|
int32_t i;
|
|
if ( preventCCvins >= 0 )
|
|
{
|
|
for (i=preventCCvins; i<numvins; i++)
|
|
{
|
|
if ( IsCCInput(tx.vin[i].scriptSig) != 0 )
|
|
return eval->Invalid("invalid CC vin");
|
|
}
|
|
}
|
|
if ( preventCCvouts >= 0 )
|
|
{
|
|
for (i=preventCCvouts; i<numvouts; i++)
|
|
{
|
|
if ( tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
|
{
|
|
fprintf(stderr,"vout.%d is CC\n",i);
|
|
return eval->Invalid("invalid CC vout");
|
|
}
|
|
}
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
bool priv2addr(char *coinaddr,uint8_t *buf33,uint8_t priv32[32])
|
|
{
|
|
CKey priv; CPubKey pk; int32_t i; uint8_t *src;
|
|
priv.SetKey32(priv32);
|
|
pk = priv.GetPubKey();
|
|
if ( buf33 != 0 )
|
|
{
|
|
src = (uint8_t *)pk.begin();
|
|
for (i=0; i<33; i++)
|
|
buf33[i] = src[i];
|
|
}
|
|
return(Getscriptaddress(coinaddr, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG));
|
|
}
|
|
|
|
std::vector<uint8_t> Mypubkey()
|
|
{
|
|
extern uint8_t NOTARY_PUBKEY33[33];
|
|
std::vector<uint8_t> pubkey; int32_t i; uint8_t *dest,*pubkey33;
|
|
pubkey33 = NOTARY_PUBKEY33;
|
|
pubkey.resize(33);
|
|
dest = pubkey.data();
|
|
for (i=0; i<33; i++)
|
|
dest[i] = pubkey33[i];
|
|
return(pubkey);
|
|
}
|
|
|
|
extern char NSPV_wifstr[],NSPV_pubkeystr[];
|
|
extern uint32_t NSPV_logintime;
|
|
#define NSPV_AUTOLOGOUT 777
|
|
|
|
bool Myprivkey(uint8_t myprivkey[])
|
|
{
|
|
char coinaddr[64],checkaddr[64]; std::string strAddress; char *dest; int32_t i,n; CBitcoinAddress address; CKeyID keyID; CKey vchSecret; uint8_t buf33[33];
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
{
|
|
if ( NSPV_logintime == 0 || time(NULL) > NSPV_logintime+NSPV_AUTOLOGOUT )
|
|
{
|
|
fprintf(stderr,"need to be logged in to get myprivkey\n");
|
|
return false;
|
|
}
|
|
vchSecret = DecodeSecret(NSPV_wifstr);
|
|
memcpy(myprivkey,vchSecret.begin(),32);
|
|
//for (i=0; i<32; i++)
|
|
// fprintf(stderr,"%02x",myprivkey[i]);
|
|
//fprintf(stderr," myprivkey %s\n",NSPV_wifstr);
|
|
memset((uint8_t *)vchSecret.begin(),0,32);
|
|
return true;
|
|
}
|
|
if ( Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG) != 0 )
|
|
{
|
|
n = (int32_t)strlen(coinaddr);
|
|
strAddress.resize(n+1);
|
|
dest = (char *)strAddress.data();
|
|
for (i=0; i<n; i++)
|
|
dest[i] = coinaddr[i];
|
|
dest[i] = 0;
|
|
if ( address.SetString(strAddress) != 0 && address.GetKeyID(keyID) != 0 )
|
|
{
|
|
#ifdef ENABLE_WALLET
|
|
if ( pwalletMain->GetKey(keyID,vchSecret) != 0 )
|
|
{
|
|
memcpy(myprivkey,vchSecret.begin(),32);
|
|
memset((uint8_t *)vchSecret.begin(),0,32);
|
|
if ( 0 )
|
|
{
|
|
for (i=0; i<32; i++)
|
|
fprintf(stderr,"0x%02x, ",myprivkey[i]);
|
|
fprintf(stderr," found privkey for %s!\n",dest);
|
|
}
|
|
if ( priv2addr(checkaddr,buf33,myprivkey) != 0 )
|
|
{
|
|
if ( buf2pk(buf33) == Mypubkey() && strcmp(checkaddr,coinaddr) == 0 )
|
|
return(true);
|
|
else printf("mismatched privkey -> addr %s vs %s\n",checkaddr,coinaddr);
|
|
}
|
|
return(false);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
fprintf(stderr,"privkey for the -pubkey= address is not in the wallet, importprivkey!\n");
|
|
return(false);
|
|
}
|
|
|
|
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv)
|
|
{
|
|
if ( unspendablepriv != 0 )
|
|
memcpy(unspendablepriv,cp->CCpriv,32);
|
|
return(pubkey2pk(ParseHex(cp->CChexstr)));
|
|
}
|
|
|
|
void CCclearvars(struct CCcontract_info *cp)
|
|
{
|
|
cp->unspendableEvalcode2 = cp->unspendableEvalcode3 = 0;
|
|
cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0;
|
|
}
|
|
|
|
int64_t CCduration(int32_t &numblocks,uint256 txid)
|
|
{
|
|
CTransaction tx; uint256 hashBlock; uint32_t txheight,txtime=0; char str[65]; CBlockIndex *pindex; int64_t duration = 0;
|
|
numblocks = 0;
|
|
if ( myGetTransaction(txid,tx,hashBlock) == 0 )
|
|
{
|
|
//fprintf(stderr,"CCduration cant find duration txid %s\n",uint256_str(str,txid));
|
|
return(0);
|
|
}
|
|
else if ( hashBlock == zeroid )
|
|
{
|
|
//fprintf(stderr,"CCduration no hashBlock for txid %s\n",uint256_str(str,txid));
|
|
return(0);
|
|
}
|
|
else if ( (pindex= hush_getblockindex(hashBlock)) == 0 || (txtime= pindex->nTime) == 0 || (txheight= pindex->GetHeight()) <= 0 )
|
|
{
|
|
fprintf(stderr,"CCduration no txtime %u or txheight.%d %p for txid %s\n",txtime,txheight,pindex,uint256_str(str,txid));
|
|
return(0);
|
|
}
|
|
else if ( (pindex= chainActive.LastTip()) == 0 || pindex->nTime < txtime || pindex->GetHeight() <= txheight )
|
|
{
|
|
if ( pindex->nTime < txtime )
|
|
fprintf(stderr,"CCduration backwards timestamps %u %u for txid %s hts.(%d %d)\n",(uint32_t)pindex->nTime,txtime,uint256_str(str,txid),txheight,(int32_t)pindex->GetHeight());
|
|
return(0);
|
|
}
|
|
numblocks = (pindex->GetHeight() - txheight);
|
|
duration = (pindex->nTime - txtime);
|
|
//fprintf(stderr,"duration %d (%u - %u) numblocks %d (%d - %d)\n",(int32_t)duration,(uint32_t)pindex->nTime,txtime,numblocks,pindex->GetHeight(),txheight);
|
|
return(duration);
|
|
}
|
|
|
|
uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid)
|
|
{
|
|
CTransaction tx; uint256 hash,mhash,bhash,hashBlock,oracletxid; int32_t len,len2,numvouts;
|
|
int64_t val,merkleht; CPubKey pk; std::vector<uint8_t>data; char str[65],str2[65];
|
|
|
|
txid = zeroid;
|
|
LogPrint(logcategory,"start reverse scan %s\n",uint256_str(str,batontxid));
|
|
while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 )
|
|
{
|
|
LogPrint(logcategory,"check %s\n",uint256_str(str,batontxid));
|
|
if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid )
|
|
{
|
|
LogPrint(logcategory,"decoded %s\n",uint256_str(str,batontxid));
|
|
if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height )
|
|
{
|
|
len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size());
|
|
len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size());
|
|
|
|
LogPrint(logcategory,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash));
|
|
if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid )
|
|
{
|
|
txid = batontxid;
|
|
LogPrint(logcategory,"set txid\n");
|
|
return(mhash);
|
|
}
|
|
else
|
|
{
|
|
LogPrint(logcategory,"missing hash\n");
|
|
return(zeroid);
|
|
}
|
|
}
|
|
else LogPrint(logcategory,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height);
|
|
batontxid = bhash;
|
|
LogPrint(logcategory,"new hash %s\n",uint256_str(str,batontxid));
|
|
} else break;
|
|
}
|
|
LogPrint(logcategory,"end of loop\n");
|
|
return(zeroid);
|
|
}
|
|
|
|
int32_t NSPV_coinaddr_inmempool(char const *logcategory,char *coinaddr,uint8_t CCflag);
|
|
|
|
int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,char *coinaddr)
|
|
{
|
|
int32_t i,n; char destaddr[64];
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
return(NSPV_coinaddr_inmempool(logcategory,coinaddr,1));
|
|
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
|
|
{
|
|
const CTransaction &tx = e.GetTx();
|
|
if ( (n= tx.vout.size()) > 0 )
|
|
{
|
|
const uint256 &txid = tx.GetHash();
|
|
for (i=0; i<n; i++)
|
|
{
|
|
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
|
|
if ( strcmp(destaddr,coinaddr) == 0 )
|
|
{
|
|
LogPrint(logcategory,"found (%s) vout in mempool\n",coinaddr);
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
extern struct NSPV_mempoolresp NSPV_mempoolresult;
|
|
extern bool NSPV_evalcode_inmempool(uint8_t evalcode,uint8_t funcid);
|
|
|
|
int32_t myGet_mempool_txs(std::vector<CTransaction> &txs,uint8_t evalcode,uint8_t funcid)
|
|
{
|
|
int i=0;
|
|
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
{
|
|
CTransaction tx; uint256 hashBlock;
|
|
|
|
NSPV_evalcode_inmempool(evalcode,funcid);
|
|
for (int i=0;i<NSPV_mempoolresult.numtxids;i++)
|
|
{
|
|
if (myGetTransaction(NSPV_mempoolresult.txids[i],tx,hashBlock)!=0) txs.push_back(tx);
|
|
}
|
|
return (NSPV_mempoolresult.numtxids);
|
|
}
|
|
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
|
|
{
|
|
txs.push_back(e.GetTx());
|
|
i++;
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
int32_t CCCointxidExists(char const *logcategory,uint256 cointxid)
|
|
{
|
|
char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock;
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
|
CCtxidaddr(txidaddr,cointxid);
|
|
SetCCtxids(addressIndex,txidaddr,true);
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
|
|
{
|
|
return(-1);
|
|
}
|
|
return(myIs_coinaddr_inmempoolvout(logcategory,txidaddr));
|
|
}
|
|
|
|
/* Get the block merkle root for a proof
|
|
* IN: proofData
|
|
* OUT: merkle root
|
|
* OUT: transaction IDS
|
|
*/
|
|
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids)
|
|
{
|
|
CMerkleBlock merkleBlock;
|
|
if (!E_UNMARSHAL(proofData, ss >> merkleBlock))
|
|
return uint256();
|
|
return merkleBlock.txn.ExtractMatches(txids);
|
|
}
|
|
|
|
extern struct NSPV_inforesp NSPV_inforesult;
|
|
int32_t hush_get_current_height()
|
|
{
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
{
|
|
return (NSPV_inforesult.height);
|
|
}
|
|
else return chainActive.LastTip()->GetHeight();
|
|
}
|
|
|
|
bool hush_txnotarizedconfirmed(uint256 txid)
|
|
{
|
|
char str[65];
|
|
int32_t confirms,notarized=0,txheight=0,currentheight=0;;
|
|
CTransaction tx;
|
|
uint256 hashBlock;
|
|
CBlockIndex *pindex;
|
|
char symbol[HUSH_SMART_CHAIN_MAXLEN],dest[HUSH_SMART_CHAIN_MAXLEN]; struct hush_state *sp;
|
|
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
{
|
|
if ( NSPV_myGetTransaction(txid,tx,hashBlock,txheight,currentheight) == 0 )
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
|
return(0);
|
|
}
|
|
else if (txheight<=0)
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d for txid %s\n",txheight,txid.ToString().c_str());
|
|
return(0);
|
|
}
|
|
else if (txheight>currentheight)
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,currentheight);
|
|
return(0);
|
|
}
|
|
confirms=1 + currentheight - txheight;
|
|
}
|
|
else
|
|
{
|
|
if ( myGetTransaction(txid,tx,hashBlock) == 0 )
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed cant find txid %s\n",txid.ToString().c_str());
|
|
return(0);
|
|
}
|
|
else if ( hashBlock == zeroid )
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed no hashBlock for txid %s\n",txid.ToString().c_str());
|
|
return(0);
|
|
}
|
|
else if ( (pindex= hush_blockindex(hashBlock)) == 0 || (txheight= pindex->GetHeight()) <= 0 )
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed no txheight.%d %p for txid %s\n",txheight,pindex,txid.ToString().c_str());
|
|
return(0);
|
|
}
|
|
else if ( (pindex= chainActive.LastTip()) == 0 || pindex->GetHeight() < txheight )
|
|
{
|
|
fprintf(stderr,"hush_txnotarizedconfirmed backwards heights for txid %s hts.(%d %d)\n",txid.ToString().c_str(),txheight,(int32_t)pindex->GetHeight());
|
|
return(0);
|
|
}
|
|
confirms=1 + pindex->GetHeight() - txheight;
|
|
}
|
|
|
|
if ((sp= hush_stateptr(symbol,dest)) != 0 && (notarized=sp->NOTARIZED_HEIGHT) > 0 && txheight > sp->NOTARIZED_HEIGHT) notarized=0;
|
|
#ifdef TESTMODE
|
|
notarized=0;
|
|
#endif //TESTMODE
|
|
if (notarized>0 && confirms > 1)
|
|
return (true);
|
|
else if (notarized==0 && confirms >= MIN_NON_NOTARIZED_CONFIRMS)
|
|
return (true);
|
|
return (false);
|
|
}
|
|
|
|
CPubKey check_signing_pubkey(CScript scriptSig)
|
|
{
|
|
bool found = false;
|
|
CPubKey pubkey;
|
|
|
|
auto findEval = [](CC *cond, struct CCVisitor _) {
|
|
bool r = false;
|
|
|
|
if (cc_typeId(cond) == CC_Secp256k1) {
|
|
*(CPubKey*)_.context=buf2pk(cond->publicKey);
|
|
r = true;
|
|
}
|
|
// false for a match, true for continue
|
|
return r ? 0 : 1;
|
|
};
|
|
|
|
CC *cond = GetCryptoCondition(scriptSig);
|
|
|
|
if (cond) {
|
|
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
|
|
bool out = !cc_visit(cond, visitor);
|
|
cc_free(cond);
|
|
|
|
if (pubkey.IsValid()) {
|
|
return pubkey;
|
|
}
|
|
}
|
|
return CPubKey();
|
|
}
|
|
|
|
|
|
// returns total of normal inputs signed with this pubkey
|
|
int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey)
|
|
{
|
|
int64_t total = 0;
|
|
for (auto vin : tx.vin) {
|
|
CTransaction vintx;
|
|
uint256 hashBlock;
|
|
if (!IsCCInput(vin.scriptSig) && myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
|
typedef std::vector<unsigned char> valtype;
|
|
std::vector<valtype> vSolutions;
|
|
txnouttype whichType;
|
|
|
|
if (Solver(vintx.vout[vin.prevout.n].scriptPubKey, whichType, vSolutions)) {
|
|
switch (whichType) {
|
|
case TX_PUBKEY:
|
|
if (pubkey == CPubKey(vSolutions[0])) // is my input?
|
|
total += vintx.vout[vin.prevout.n].nValue;
|
|
break;
|
|
case TX_PUBKEYHASH:
|
|
if (pubkey.GetID() == CKeyID(uint160(vSolutions[0]))) // is my input?
|
|
total += vintx.vout[vin.prevout.n].nValue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
// returns total of CC inputs signed with this pubkey
|
|
int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey)
|
|
{
|
|
int64_t total = 0;
|
|
for (auto vin : tx.vin) {
|
|
if (IsCCInput(vin.scriptSig)) {
|
|
CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig);
|
|
if (vinPubkey.IsValid()) {
|
|
if (vinPubkey == pubkey) {
|
|
CTransaction vintx;
|
|
uint256 hashBlock;
|
|
if (myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
|
total += vintx.vout[vin.prevout.n].nValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
|
|
{
|
|
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector<uint8_t> origpubkey;
|
|
height = HUSH_CONNECTING;
|
|
if ( HUSH_CONNECTING < 0 ) // always comes back with > 0 for final confirmation
|
|
return(true);
|
|
if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < HUSH_CCACTIVATE )
|
|
return eval->Invalid("CC are disabled or not active yet");
|
|
if ( (HUSH_CONNECTING & (1<<30)) != 0 )
|
|
{
|
|
from_mempool = 1;
|
|
height &= ((1<<30) - 1);
|
|
}
|
|
if (cp->validate == NULL)
|
|
return eval->Invalid("validation not supported for eval code");
|
|
|
|
//fprintf(stderr,"HUSH_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,HUSH_CCACTIVATE);
|
|
// there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example
|
|
//txid = ctx.GetHash();
|
|
//if ( txid == cp->prevtxid )
|
|
// return(true);
|
|
//fprintf(stderr,"process CC %02x\n",cp->evalcode);
|
|
CCclearvars(cp);
|
|
if ( paramsNull.size() != 0 ) // Don't expect params
|
|
return eval->Invalid("Cannot have params");
|
|
//else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses
|
|
// return eval->Invalid("no-vouts");
|
|
else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 )
|
|
{
|
|
//fprintf(stderr,"done CC %02x\n",cp->evalcode);
|
|
//cp->prevtxid = txid;
|
|
return(true);
|
|
}
|
|
//fprintf(stderr,"invalid CC %02x\n",cp->evalcode);
|
|
return(false);
|
|
}
|
|
|
|
extern struct CCcontract_info CCinfos[0x100];
|
|
extern std::string MYCCLIBNAME;
|
|
bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn);
|
|
|
|
bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector<uint8_t> paramsNull,const CTransaction &txTo,unsigned int nIn)
|
|
{
|
|
uint8_t evalcode; int32_t height,from_mempool; struct CCcontract_info *cp;
|
|
if ( ASSETCHAINS_CCLIB != MYCCLIBNAME )
|
|
{
|
|
fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME.c_str());
|
|
return eval->Invalid("-ac_cclib name mismatches myname");
|
|
}
|
|
height = HUSH_CONNECTING;
|
|
if ( HUSH_CONNECTING < 0 ) // always comes back with > 0 for final confirmation
|
|
return(true);
|
|
if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < HUSH_CCACTIVATE )
|
|
return eval->Invalid("CC are disabled or not active yet");
|
|
if ( (HUSH_CONNECTING & (1<<30)) != 0 )
|
|
{
|
|
from_mempool = 1;
|
|
height &= ((1<<30) - 1);
|
|
}
|
|
evalcode = cond->code[0];
|
|
if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER )
|
|
{
|
|
cp = &CCinfos[(int32_t)evalcode];
|
|
if ( cp->didinit == 0 )
|
|
{
|
|
if ( CClib_initcp(cp,evalcode) == 0 )
|
|
cp->didinit = 1;
|
|
else return eval->Invalid("unsupported CClib evalcode");
|
|
}
|
|
CCclearvars(cp);
|
|
if ( paramsNull.size() != 0 ) // Don't expect params
|
|
return eval->Invalid("Cannot have params");
|
|
else if ( CClib_validate(cp,height,eval,txTo,nIn) != 0 )
|
|
return(true);
|
|
return(false); //eval->Invalid("error in CClib_validate");
|
|
}
|
|
return eval->Invalid("cclib CC must have evalcode between 16 and 127");
|
|
}
|
|
|