Last active
November 17, 2018 18:14
-
-
Save lukem512/6382f9f2f8446f3e35cf to your computer and use it in GitHub Desktop.
Ban address in Bitcoin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Check that the destination is not banned | |
bool IsDestinationBanned(CScript scriptPubKey) | |
{ | |
txnouttype typeRet; | |
vector<CTxDestination> addressRet; | |
int nRequiredRet; | |
if (!ExtractDestinations(scriptPubKey, typeRet, addressRet, nRequiredRet)) | |
// Could not extract destinations | |
return true; | |
BOOST_FOREACH(CTxDestination address, addressRet) | |
BOOST_FOREACH(std::string bannedAddress, bannedAddresses) | |
if (address == CBitcoinAddress(bannedAddress).Get()) | |
// Destination address is banned | |
return true; | |
return false; | |
} | |
bool CTransaction::IsInputBanned(const CTxIn& input, CCoinsViewCache &mapInputs) const | |
{ | |
// Determine script type | |
const CTxOut& prev = GetOutputFor(input, mapInputs); | |
const CScript& prevScript = prev.scriptPubKey; | |
vector<vector<unsigned char> > vSolutions; | |
txnouttype whichType; | |
if (!Solver(prevScript, whichType, vSolutions)) | |
{ | |
printf("IsInputBanned() : Solver returned false\n"); | |
return true; | |
} | |
// Evaluate P2PKH script | |
// <sig> <pubkey> | |
if (whichType == TX_PUBKEYHASH) | |
{ | |
std::vector<std::vector<unsigned char> > stack; | |
CTransaction txTo; | |
unsigned int nIn, flags, nHashType; | |
if (!EvalScript(stack, input.scriptSig, txTo, nIn, flags, nHashType)) | |
{ | |
printf("IsInputBanned() : EvalScript returned false\n"); | |
return true; | |
} | |
// Expose pubkey | |
vector<unsigned char>& vchPubKey = stack.at(stack.size()+(-1)); | |
// Take pubkey and find address | |
CPubKey pubkey(vchPubKey); | |
if (!pubkey.IsValid()) | |
{ | |
printf("IsInputBanned() : pubKey is not valid\n"); | |
return true; | |
} | |
CBitcoinAddress address; | |
address.Set(pubkey.GetID()); | |
// Check address against blacklist | |
BOOST_FOREACH(std::string bannedAddress, bannedAddresses) | |
{ | |
if (address.Get() == CBitcoinAddress(bannedAddress).Get()) | |
{ | |
printf("IsInputBanned() : sender address %s is BANNED\n", address.ToString().c_str()); | |
return true; | |
} | |
} | |
} | |
// Not banned! | |
return false; | |
} | |
bool CTransaction::CheckTransaction() const | |
{ | |
// Basic checks that don't depend on any context | |
if (vin.empty()) | |
return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); | |
if (vout.empty()) | |
return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); | |
// Size limits | |
if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) | |
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); | |
// Check for negative or overflow output values | |
int64 nValueOut = 0; | |
for (unsigned int i = 0; i < vout.size(); i++) | |
{ | |
const CTxOut& txout = vout[i]; | |
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) | |
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); | |
// ppcoin: enforce minimum output amount | |
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) | |
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); | |
if (txout.nValue > MAX_MONEY) | |
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); | |
nValueOut += txout.nValue; | |
if (!MoneyRange(nValueOut)) | |
return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); | |
// Ensure destination isn't banned address | |
if (IsDestinationBanned(txout)) | |
return state.DoS(100, error("CTransaction::CheckTransaction() : destination address is banned")); | |
} | |
// Check for duplicate inputs | |
set<COutPoint> vInOutPoints; | |
BOOST_FOREACH(const CTxIn& txin, vin) | |
{ | |
if (vInOutPoints.count(txin.prevout)) | |
return false; | |
vInOutPoints.insert(txin.prevout); | |
} | |
if (IsCoinBase()) | |
{ | |
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) | |
return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); | |
} | |
else | |
{ | |
BOOST_FOREACH(const CTxIn& txin, vin) | |
{ | |
if (txin.prevout.IsNull()) | |
return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); | |
// Check for banned sender address | |
if (IsSenderBanned(txin)) | |
return state.DoS(100, error("CTransaction::CheckTransaction() : banned address in input")) | |
} | |
} | |
return true; | |
} | |
bool CTransaction::CheckInputs(CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const | |
{ | |
if (!IsCoinBase()) | |
{ | |
if (pvChecks) | |
pvChecks->reserve(vin.size()); | |
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier | |
// for an attacker to attempt to split the network. | |
if (!HaveInputs(inputs)) | |
return state.Invalid(error("CheckInputs() : %s inputs unavailable", GetHash().ToString().c_str())); | |
// While checking, GetBestBlock() refers to the parent block. | |
// This is also true for mempool checks. | |
int nSpendHeight = inputs.GetBestBlock()->nHeight + 1; | |
int64 nValueIn = 0; | |
int64 nFees = 0; | |
for (unsigned int i = 0; i < vin.size(); i++) | |
{ | |
const COutPoint &prevout = vin[i].prevout; | |
const CCoins &coins = inputs.GetCoins(prevout.hash); | |
// If prev is coinbase, check that it's matured | |
if (coins.IsCoinBase()) { | |
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) | |
return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight)); | |
} | |
// Check for negative or overflow input values | |
nValueIn += coins.vout[prevout.n].nValue; | |
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) | |
return state.DoS(100, error("CheckInputs() : txin values out of range")); | |
// Check for banned inputs | |
if (IsInputBanned(vin[i], inputs)) | |
return state.DoS(100, error("CheckInputs() : input banned")); | |
} | |
if (nValueIn < GetValueOut()) | |
return state.DoS(100, error("CheckInputs() : %s value in < value out", GetHash().ToString().c_str())); | |
// Tally transaction fees | |
int64 nTxFee = nValueIn - GetValueOut(); | |
if (nTxFee < 0) | |
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().c_str())); | |
nFees += nTxFee; | |
if (!MoneyRange(nFees)) | |
return state.DoS(100, error("CheckInputs() : nFees out of range")); | |
// The first loop above does all the inexpensive checks. | |
// Only if ALL inputs pass do we perform expensive ECDSA signature checks. | |
// Helps prevent CPU exhaustion attacks. | |
// Skip ECDSA signature verification when connecting blocks | |
// before the last block chain checkpoint. This is safe because block merkle hashes are | |
// still computed and checked, and any change will be caught at the next checkpoint. | |
if (fScriptChecks) { | |
for (unsigned int i = 0; i < vin.size(); i++) { | |
const COutPoint &prevout = vin[i].prevout; | |
const CCoins &coins = inputs.GetCoins(prevout.hash); | |
// Verify signature | |
CScriptCheck check(coins, *this, i, flags, 0); | |
if (pvChecks) { | |
pvChecks->push_back(CScriptCheck()); | |
check.swap(pvChecks->back()); | |
} else if (!check()) { | |
if (flags & SCRIPT_VERIFY_STRICTENC) { | |
// For now, check whether the failure was caused by non-canonical | |
// encodings or not; if so, don't trigger DoS protection. | |
CScriptCheck check(coins, *this, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0); | |
if (check()) | |
return state.Invalid(); | |
} | |
return state.DoS(100,false); | |
} | |
} | |
} | |
} | |
return true; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Banned addresses | |
static const std::string bannedAddresses[] = { | |
"1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v" | |
}; | |
// Declare beneath CTxOut declaration | |
bool IsDestinationBanned(CScript scriptPubKey); | |
bool IsDestinationBanned(CTxOut txout); | |
class CTransaction | |
{ | |
public: | |
// Check whether an input is on the blacklist | |
bool IsInputBanned(const CTxIn& input, CCoinsViewCache &mapInputs) const; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl) | |
{ | |
int64 nValue = 0; | |
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) | |
{ | |
if (nValue < 0) | |
return false; | |
nValue += s.second; | |
// Ensure destination isn't banned address | |
if (IsDestinationBanned(s.first)) | |
{ | |
strFailReason = _("Destination address is banned"); | |
return false; | |
} | |
} | |
if (vecSend.empty() || nValue < 0) | |
return false; | |
wtxNew.BindWallet(this); | |
{ | |
LOCK2(cs_main, cs_wallet); | |
// txdb must be opened before the mapWallet lock | |
CTxDB txdb("r"); | |
{ | |
nFeeRet = nTransactionFee; | |
loop | |
{ | |
wtxNew.vin.clear(); | |
wtxNew.vout.clear(); | |
wtxNew.fFromMe = true; | |
int64 nTotalValue = nValue + nFeeRet; | |
double dPriority = 0; | |
// vouts to the payees | |
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) | |
wtxNew.vout.push_back(CTxOut(s.second, s.first)); | |
// Choose coins to use | |
set<pair<const CWalletTx*,unsigned int> > setCoins; | |
int64 nValueIn = 0; | |
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) | |
return false; | |
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) | |
{ | |
int64 nCredit = pcoin.first->vout[pcoin.second].nValue; | |
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); | |
} | |
int64 nChange = nValueIn - nValue - nFeeRet; | |
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE | |
// or until nChange becomes zero | |
// NOTE: this depends on the exact behaviour of GetMinFee | |
if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) | |
{ | |
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); | |
nChange -= nMoveToFee; | |
nFeeRet += nMoveToFee; | |
} | |
// ppcoin: sub-cent change is moved to fee | |
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT) | |
{ | |
nFeeRet += nChange; | |
nChange = 0; | |
} | |
if (nChange > 0) | |
{ | |
// Fill a vout to ourself | |
// TODO: pass in scriptChange instead of reservekey so | |
// change transaction isn't always pay-to-bitcoin-address | |
CScript scriptChange; | |
// coin control: send change to custom address | |
if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange)) | |
scriptChange.SetDestination(coinControl->destChange); | |
// no coin control: send change to newly generated address | |
else | |
{ | |
// Note: We use a new key here to keep it from being obvious which side is the change. | |
// The drawback is that by not reusing a previous key, the change may be lost if a | |
// backup is restored, if the backup doesn't have the new private key for the change. | |
// If we reused the old key, it would be possible to add code to look for and | |
// rediscover unknown transactions that were written with keys of ours to recover | |
// post-backup change. | |
// Reserve a new key pair from key pool | |
CPubKey vchPubKey = reservekey.GetReservedKey(); | |
scriptChange.SetDestination(vchPubKey.GetID()); | |
} | |
// Insert change txn at random position: | |
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); | |
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); | |
} | |
else | |
reservekey.ReturnKey(); | |
// Fill vin | |
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) | |
wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); | |
// Sign | |
int nIn = 0; | |
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) | |
if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) | |
return false; | |
// Limit size | |
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); | |
if (nBytes >= MAX_BLOCK_SIZE_GEN/5) | |
return false; | |
dPriority /= nBytes; | |
// Check that enough fee is included | |
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); | |
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND, nBytes); | |
if (nFeeRet < max(nPayFee, nMinFee)) | |
{ | |
nFeeRet = max(nPayFee, nMinFee); | |
continue; | |
} | |
// Fill vtxPrev by copying from previous transactions vtxPrev | |
wtxNew.AddSupportingTransactions(txdb); | |
wtxNew.fTimeReceivedIsTxTime = true; | |
break; | |
} | |
} | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
CWallet::CreateTransaction()
andCTransaction::CheckTransaction()
contain a check for a banned destination. This check is the functionIsDestinationBanned()
which can be passed aCTxOut
or aCScript
scriptPubKey.CTransaction::CheckInputs()
contains a check for a banned input. This check is the funtionCTransaction::IsInputBanned()
which is passed aCTxOut
. This only checks pay-to-pubkeyhash inputs as, currently, the vast majority of inputs are of this type. If other input types need to be blocked then these must be handled separately.