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.
340 lines
14 KiB
340 lines
14 KiB
// Copyright (c) 2016-2024 The Hush developers
|
|
// Distributed under the GPLv3 software license, see the accompanying
|
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
/******************************************************************************
|
|
* Copyright © 2014-2019 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
#include "CCinclude.h"
|
|
#include "key_io.h"
|
|
|
|
std::vector<CPubKey> NULL_pubkeys;
|
|
struct NSPV_CCmtxinfo NSPV_U;
|
|
|
|
/* see description to function definition in CCinclude.h */
|
|
bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey)
|
|
{
|
|
#ifdef ENABLE_WALLET
|
|
CTransaction txNewConst(mtx); SignatureData sigdata; const CKeyStore& keystore = *pwalletMain;
|
|
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
|
|
if ( ProduceSignature(TransactionSignatureCreator(&keystore,&txNewConst,vini,utxovalue,SIGHASH_ALL),scriptPubKey,sigdata,consensusBranchId) != 0 )
|
|
{
|
|
UpdateTransaction(mtx,vini,sigdata);
|
|
return(true);
|
|
} else fprintf(stderr,"signing error for SignTx vini.%d %.8f\n",vini,(double)utxovalue/COIN);
|
|
#endif
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn.
|
|
|
|
This allows the contract transaction functions to create the appropriate vins and vouts and have FinalizeCCTx create a properly signed transaction.
|
|
|
|
By using -addressindex=1, it allows tracking of all the CC addresses
|
|
*/
|
|
std::string FinalizeCCTx(uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector<CPubKey> pubkeys)
|
|
{
|
|
return std::string("");
|
|
}
|
|
|
|
|
|
// extended version that supports signInfo object with conds to vins map for remote cc calls
|
|
UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector<CPubKey> pubkeys)
|
|
{
|
|
UniValue result(UniValue::VOBJ);
|
|
return result;
|
|
}
|
|
|
|
void NSPV_CCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool ccflag);
|
|
void NSPV_CCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &txids,char *coinaddr,bool ccflag);
|
|
void NSPV_CCtxids(std::vector<uint256> &txids,char *coinaddr,bool ccflag, uint8_t evalcode,uint256 filtertxid, uint8_t func);
|
|
|
|
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool ccflag)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct CC_utxo utxos[],int32_t numunspents,int64_t value)
|
|
{
|
|
int32_t i,abovei,belowi; int64_t above,below,gap,atx_value;
|
|
abovei = belowi = -1;
|
|
for (above=below=i=0; i<numunspents; i++)
|
|
{
|
|
// Filter to randomly pick utxo to avoid conflicts, and having multiple CC choose the same ones.
|
|
//if ( numunspents > 200 ) {
|
|
// if ( (rand() % 100) < 90 )
|
|
// continue;
|
|
//}
|
|
if ( (atx_value= utxos[i].nValue) <= 0 )
|
|
continue;
|
|
if ( atx_value == value )
|
|
{
|
|
*aboveip = *belowip = i;
|
|
*abovep = *belowp = 0;
|
|
return(i);
|
|
}
|
|
else if ( atx_value > value )
|
|
{
|
|
gap = (atx_value - value);
|
|
if ( above == 0 || gap < above )
|
|
{
|
|
above = gap;
|
|
abovei = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gap = (value - atx_value);
|
|
if ( below == 0 || gap < below )
|
|
{
|
|
below = gap;
|
|
belowi = i;
|
|
}
|
|
}
|
|
//printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below));
|
|
}
|
|
*aboveip = abovei;
|
|
*abovep = above;
|
|
*belowip = belowi;
|
|
*belowp = below;
|
|
//printf("above.%d below.%d\n",abovei,belowi);
|
|
if ( abovei >= 0 && belowi >= 0 )
|
|
{
|
|
if ( above < (below >> 1) )
|
|
return(abovei);
|
|
else return(belowi);
|
|
}
|
|
else if ( abovei >= 0 )
|
|
return(abovei);
|
|
else return(belowi);
|
|
}
|
|
|
|
int64_t AddNormalinputsLocal(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs)
|
|
{
|
|
int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector<COutput> vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up;
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U));
|
|
|
|
// if (mypk != pubkey2pk(Mypubkey())) //remote superlite mypk, do not use wallet since it is not locked for non-equal pks (see rpcs with nspv support)!
|
|
// return(AddNormalinputs3(mtx, mypk, total, maxinputs));
|
|
|
|
#ifdef ENABLE_WALLET
|
|
assert(pwalletMain != NULL);
|
|
const CKeyStore& keystore = *pwalletMain;
|
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
|
|
utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos));
|
|
if ( maxinputs > CC_MAXVINS )
|
|
maxinputs = CC_MAXVINS;
|
|
if ( maxinputs > 0 )
|
|
threshold = total/maxinputs;
|
|
else threshold = total;
|
|
sum = 0;
|
|
BOOST_FOREACH(const COutput& out, vecOutputs)
|
|
{
|
|
if ( out.fSpendable != 0 && out.tx->vout[out.i].nValue >= threshold )
|
|
{
|
|
txid = out.tx->GetHash();
|
|
vout = out.i;
|
|
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
|
{
|
|
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
|
|
if ( mtx.vin.size() > 0 )
|
|
{
|
|
for (i=0; i<mtx.vin.size(); i++)
|
|
if ( txid == mtx.vin[i].prevout.hash && vout == mtx.vin[i].prevout.n )
|
|
break;
|
|
if ( i != mtx.vin.size() )
|
|
continue;
|
|
}
|
|
if ( n > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
if ( txid == utxos[i].txid && vout == utxos[i].vout )
|
|
break;
|
|
if ( i != n )
|
|
continue;
|
|
}
|
|
if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
|
{
|
|
up = &utxos[n++];
|
|
up->txid = txid;
|
|
up->nValue = out.tx->vout[out.i].nValue;
|
|
up->vout = vout;
|
|
sum += up->nValue;
|
|
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
|
|
if ( n >= maxinputs || sum >= total )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
remains = total;
|
|
for (i=0; i<maxinputs && n>0; i++)
|
|
{
|
|
below = above = 0;
|
|
abovei = belowi = -1;
|
|
if ( CC_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 )
|
|
{
|
|
printf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
|
|
free(utxos);
|
|
return(0);
|
|
}
|
|
if ( belowi < 0 || abovei >= 0 )
|
|
ind = abovei;
|
|
else ind = belowi;
|
|
if ( ind < 0 )
|
|
{
|
|
printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
|
|
free(utxos);
|
|
return(0);
|
|
}
|
|
up = &utxos[ind];
|
|
mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript()));
|
|
totalinputs += up->nValue;
|
|
remains -= up->nValue;
|
|
utxos[ind] = utxos[--n];
|
|
memset(&utxos[n],0,sizeof(utxos[n]));
|
|
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
|
if ( totalinputs >= total || (i+1) >= maxinputs )
|
|
break;
|
|
}
|
|
free(utxos);
|
|
if ( totalinputs >= total )
|
|
{
|
|
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
|
|
return(totalinputs);
|
|
}
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
// always uses -pubkey param as mypk
|
|
int64_t AddNormalinputs2(CMutableTransaction &mtx, int64_t total, int32_t maxinputs)
|
|
{
|
|
CPubKey mypk = pubkey2pk(Mypubkey());
|
|
return AddNormalinputsRemote(mtx, mypk, total, maxinputs);
|
|
}
|
|
|
|
// has additional mypk param for nspv calls
|
|
int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs)
|
|
{
|
|
int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up;
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
if ( HUSH_NSPV_SUPERLITE )
|
|
return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U));
|
|
utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos));
|
|
if ( maxinputs > CC_MAXVINS )
|
|
maxinputs = CC_MAXVINS;
|
|
if ( maxinputs > 0 )
|
|
threshold = total/maxinputs;
|
|
else threshold = total;
|
|
sum = 0;
|
|
Getscriptaddress(coinaddr,CScript() << vscript_t(mypk.begin(), mypk.end()) << OP_CHECKSIG);
|
|
SetCCunspents(unspentOutputs,coinaddr,false);
|
|
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;
|
|
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
|
{
|
|
//fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout);
|
|
if ( mtx.vin.size() > 0 )
|
|
{
|
|
for (i=0; i<mtx.vin.size(); i++)
|
|
if ( txid == mtx.vin[i].prevout.hash && vout == mtx.vin[i].prevout.n )
|
|
break;
|
|
if ( i != mtx.vin.size() )
|
|
continue;
|
|
}
|
|
if ( n > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
if ( txid == utxos[i].txid && vout == utxos[i].vout )
|
|
break;
|
|
if ( i != n )
|
|
continue;
|
|
}
|
|
if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
|
{
|
|
up = &utxos[n++];
|
|
up->txid = txid;
|
|
up->nValue = it->second.satoshis;
|
|
up->vout = vout;
|
|
sum += up->nValue;
|
|
//fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos);
|
|
if ( n >= maxinputs || sum >= total )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
remains = total;
|
|
for (i=0; i<maxinputs && n>0; i++)
|
|
{
|
|
below = above = 0;
|
|
abovei = belowi = -1;
|
|
if ( CC_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 )
|
|
{
|
|
printf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN);
|
|
free(utxos);
|
|
return(0);
|
|
}
|
|
if ( belowi < 0 || abovei >= 0 )
|
|
ind = abovei;
|
|
else ind = belowi;
|
|
if ( ind < 0 )
|
|
{
|
|
printf("error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind);
|
|
free(utxos);
|
|
return(0);
|
|
}
|
|
up = &utxos[ind];
|
|
mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript()));
|
|
totalinputs += up->nValue;
|
|
remains -= up->nValue;
|
|
utxos[ind] = utxos[--n];
|
|
memset(&utxos[n],0,sizeof(utxos[n]));
|
|
//fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs);
|
|
if ( totalinputs >= total || (i+1) >= maxinputs )
|
|
break;
|
|
}
|
|
free(utxos);
|
|
if ( totalinputs >= total )
|
|
{
|
|
//fprintf(stderr,"return totalinputs %.8f\n",(double)totalinputs/COIN);
|
|
return(totalinputs);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs,bool remote)
|
|
{
|
|
if (!remote) return (AddNormalinputsLocal(mtx,mypk,total,maxinputs));
|
|
else return (AddNormalinputsRemote(mtx,mypk,total,maxinputs));
|
|
}
|
|
|
|
void AddSigData2UniValue(UniValue &sigdata, int32_t vini, UniValue& ccjson, std::string sscriptpubkey, int64_t amount)
|
|
{
|
|
UniValue elem(UniValue::VOBJ);
|
|
elem.push_back(Pair("vin", vini));
|
|
if (!ccjson.empty())
|
|
elem.push_back(Pair("cc", ccjson));
|
|
if (!sscriptpubkey.empty())
|
|
elem.push_back(Pair("scriptPubKey", sscriptpubkey));
|
|
elem.push_back(Pair("amount", amount));
|
|
sigdata.push_back(elem);
|
|
}
|
|
|