diff --git a/assets/tools/asset_audit.py b/assets/tools/asset_audit.py index 4436a568ab..0944681b22 100755 --- a/assets/tools/asset_audit.py +++ b/assets/tools/asset_audit.py @@ -6,18 +6,26 @@ # # -- NOTE: This script requires Python, PIP, and bitcoin-rpc to be installed. # To install bitcoin-rpc run the following command from the terminal: +# pip install wheel # pip install python-bitcoinrpc +# +# To use sendgid notification (optional): +# pip3 install sendgrid +# export SENDGRID_API_KEY="" +# set notification_emails variable below - +import os import subprocess import json +import logging #Set this to your raven-cli program -cli = "./src/raven-cli" +cli = "raven-cli" mode = "-main" -rpc_port = 8767 +rpc_port = 8766 + #mode = "-testnet" #rpc_port = 18770 #mode = "-regtest" @@ -27,6 +35,9 @@ rpc_user = 'rpcuser' rpc_pass = 'rpcpass555' +#Set this e-mail address, and SENDGRID_API_KEY env variable for notifications +notification_emails='test@example.com' +send_alerts_on_success = True #Set to True if you want email notification on success def listassets(filter): rpc_connection = get_rpc_connection() @@ -59,6 +70,36 @@ def get_rpc_connection(): rpc_connection = AuthServiceProxy(connection) return(rpc_connection) +def log_failure(err): + logging.error(err) + +def send_notification(notification_to_emails, notification_subject, notification_content): + sendgrid_api_key = '' + if "SENDGRID_API_KEY" in os.environ: + sendgrid_api_key = os.environ.get('SENDGRID_API_KEY') + #print("Key="+sendgrid_api_key) + else: + print("Must set SENDGRID_API_KEY environment to use e-mail notification.") + return + + import sendgrid + from sendgrid.helpers.mail import Content, Email, Mail + + message = Mail( + from_email='asset_audit_notifier@example.com', + to_emails=notification_to_emails, + subject=notification_subject, + html_content=notification_content) + + try: + sg = sendgrid.SendGridAPIClient(sendgrid_api_key) + response = sg.send(message) + #print(response.status_code) + #print(response.body) + #print(response.headers) + except: + print(e.message) + def audit(filter): assets = listassets(filter) @@ -68,6 +109,7 @@ def audit(filter): count = 0 max_dist_asset_name = "" max_dist_address_count = 0 + audits_failed = 0 for asset, properties in assets.items(): count=count+1 total_issued = 0 @@ -88,9 +130,11 @@ def audit(filter): number_of_addresses += len(address_qtys) for address, qty in address_qtys.items(): - #print(address + " -> " + str(qty)) + #print(address + "," + str(qty)) total_for_asset += qty + + # If the number of address is less than 50000, end the loop if len(address_qtys) < 50000: loop = False @@ -108,17 +152,28 @@ def audit(filter): print("Audit PASSED for " + asset) print("") else: + audits_failed += 1 print("Audit FAILED for " + asset) - exit() + msg = "Audit FAILED for " + asset + " Issued="+str(total_issued)+ " Total="+str(total_for_asset) + log_failure(msg) + send_notification(notification_emails, "Ravencoin Asset Audit Failed", msg) + #exit(-1) if len(assets) == count: print("All " + str(len(assets)) + " assets audited.") print("Stats:") print(" Max Distribed Asset: " + max_dist_asset_name + " with " + str(max_dist_address_count) + " addresses.") - + if (send_alerts_on_success and audits_failed == 0): + send_notification(notification_emails, "Ravencoin Asset Audit Success", "All " + str(len(assets)) + " assets audited.") if mode == "-regtest": #If regtest then mine our own blocks import os os.system(cli + " " + mode + " generate 400") -audit("*") #Set to "*" for all. +#### Uncomment these lines to test e-mail notification ### +#send_notification(notification_emails, "Test Subject", "Test Message") +#exit() +########################################################## + +logging.basicConfig(filename='failures.txt', format="%(asctime)-15s %(message)s", level=logging.INFO) +audit("*") #Set to "*" for all, or set a specific asset, or 'B*' diff --git a/configure.ac b/configure.ac index b82b208447..1b5ceaff93 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) -define(_CLIENT_VERSION_MINOR, 2) -define(_CLIENT_VERSION_REVISION, 1) +define(_CLIENT_VERSION_MINOR, 3) +define(_CLIENT_VERSION_REVISION, 0) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2020) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 14d595dd42..6cad9837fd 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -156,6 +156,12 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nTimeout = 1624989600; // UTC: Mon Jun 29 2021 18:00:00 consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].bit = 10; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nStartTime = 1597341600; // UTC: Thu Aug 13 2020 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 1628877600; // UTC: Fri Aug 13 2021 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 2016; + // The best chain should have at least this much work consensus.nMinimumChainWork = uint256S("000000000000000000000000000000000000000000000020d4ac871fb7009b63"); // Block 1186833 @@ -312,6 +318,11 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nTimeout = 1624989600; // UTC: Mon Jun 29 2021 18:00:00 consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].bit = 10; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nStartTime = 1597341600; // UTC: Thu Aug 13 2020 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 1628877600; // UTC: Fri Aug 13 2021 18:00:00 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 1411; // Approx 70% of 2016 + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 2016; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000168050db560b4"); @@ -527,6 +538,11 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nTimeout = 999999999999ULL; consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideRuleChangeActivationThreshold = 108; consensus.vDeployments[Consensus::DEPLOYMENT_ENFORCE_VALUE].nOverrideMinerConfirmationWindow = 144; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].bit = 10; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideRuleChangeActivationThreshold = 400; + consensus.vDeployments[Consensus::DEPLOYMENT_COINBASE_ASSETS].nOverrideMinerConfirmationWindow = 500; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/coins.cpp b/src/coins.cpp index 09105f4808..d19b20a150 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -264,14 +264,15 @@ void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, uint2 if (assetsCache) { CAssetOutputEntry assetData; if (GetAssetData(tx.vout[i].scriptPubKey, assetData)) { - if (assetData.type == TX_TRANSFER_ASSET && !tx.vout[i].scriptPubKey.IsUnspendable()) { - CAssetTransfer assetTransfer; - std::string address; - if (!TransferAssetFromScript(tx.vout[i].scriptPubKey, assetTransfer, address)) - LogPrintf( - "%s : ERROR - Received a coin that was a Transfer Asset but failed to get the transfer object from the scriptPubKey. CTxOut: %s\n", - __func__, tx.vout[i].ToString()); + // If this is a transfer asset, and the amount is greater than zero + // We want to make sure it is added to the asset addresses database if (fAssetIndex == true) + if (assetData.type == TX_TRANSFER_ASSET && assetData.nAmount > 0) { + // Create the objects needed from the assetData + CAssetTransfer assetTransfer(assetData.assetName, assetData.nAmount, assetData.message, assetData.expireTime); + std::string address = EncodeDestination(assetData.destination); + + // Add the transfer asset data to the asset cache if (!assetsCache->AddTransferAsset(assetTransfer, address, COutPoint(txid, i), tx.vout[i])) LogPrintf("%s : ERROR - Failed to add transfer asset CTxOut: %s\n", __func__, tx.vout[i].ToString()); diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index e84f0a4cc2..69b6c3be38 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -38,6 +38,7 @@ UNUSED_VAR static bool fAssetsIsActive = false; UNUSED_VAR static bool fRip5IsActive = false; UNUSED_VAR static bool fTransferScriptIsActive = false; UNUSED_VAR static bool fEnforcedValuesIsActive = false; +UNUSED_VAR static bool fCheckCoinbaseAssetsIsActive = false; unsigned int GetMaxBlockWeight(); unsigned int GetMaxBlockSerializedSize(); diff --git a/src/consensus/params.h b/src/consensus/params.h index cac538e4e4..6b6a738c2a 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -20,6 +20,7 @@ enum DeploymentPos DEPLOYMENT_MSG_REST_ASSETS, // Delpoyment of RIP5 and Restricted assets DEPLOYMENT_TRANSFER_SCRIPT_SIZE, DEPLOYMENT_ENFORCE_VALUE, + DEPLOYMENT_COINBASE_ASSETS, // DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. // DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147. // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index abb4c24f69..f79e2c875e 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -378,6 +378,15 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); + + if (AreCoinbaseCheckAssetsDeployed()) { + for (auto vout : tx.vout) { + if (vout.scriptPubKey.IsAssetScript() || vout.scriptPubKey.IsNullAsset()) { + return state.DoS(0, error("%s: coinbase contains asset transaction", __func__), + REJECT_INVALID, "bad-txns-coinbase-contains-asset-txes"); + } + } + } } else { @@ -578,7 +587,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c } } - const CAmount value_out = tx.GetValueOut(); + const CAmount value_out = tx.GetValueOut(AreEnforcedValuesDeployed()); if (nValueIn < value_out) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)), tx.GetHash()); diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index c1062d6c12..7cf0dff3f7 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -89,16 +89,19 @@ CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VER CTransaction::CTransaction(const CMutableTransaction &tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CAmount CTransaction::GetValueOut() const +CAmount CTransaction::GetValueOut(const bool fAreEnforcedValues) const { CAmount nValueOut = 0; for (const auto& tx_out : vout) { - - // Because we don't want to deal with assets messing up this calculation - // If this is an asset tx, we should move onto the next output in the transaction - // This will also help with processing speed of transaction that contain a large amounts of asset outputs in a transaction - if (tx_out.scriptPubKey.IsAssetScript()) - continue; + + // Stop doing this check after Enforced Values BIP goes active + if (!fAreEnforcedValues) { + // Because we don't want to deal with assets messing up this calculation + // If this is an asset tx, we should move onto the next output in the transaction + // This will also help with processing speed of transaction that contain a large amounts of asset outputs in a transaction + if (tx_out.scriptPubKey.IsAssetScript()) + continue; + } nValueOut += tx_out.nValue; if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut)) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index d855cf538c..6b76f1bf9f 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -325,7 +325,7 @@ class CTransaction uint256 GetWitnessHash() const; // Return sum of txouts. - CAmount GetValueOut() const; + CAmount GetValueOut(const bool fAreEnforcedValues = true) const; // GetValueIn() is a method on CCoinsViewCache, because // inputs must be known to compute value in. diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 9d3218aa49..e2b9f24d0a 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -216,7 +216,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Total credit") + ": " + RavenUnits::formatHtmlWithUnit(unit, nValue) + "
"; } - CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); + CAmount nTxFee = nDebit - wtx.tx->GetValueOut(AreEnforcedValuesDeployed()); if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + RavenUnits::formatHtmlWithUnit(unit, -nTxFee) + "
"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index cd6d06c103..9ca6bdae73 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -120,7 +120,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); + CAmount nTxFee = nDebit - wtx.tx->GetValueOut(AreEnforcedValuesDeployed()); for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 015aae9f40..53dce9c5ba 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1503,6 +1503,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) BIP9SoftForkDescPushBack(bip9_softforks, "messaging_restricted", consensusParams, Consensus::DEPLOYMENT_MSG_REST_ASSETS); BIP9SoftForkDescPushBack(bip9_softforks, "transfer_script", consensusParams, Consensus::DEPLOYMENT_TRANSFER_SCRIPT_SIZE); BIP9SoftForkDescPushBack(bip9_softforks, "enforce", consensusParams, Consensus::DEPLOYMENT_ENFORCE_VALUE); + BIP9SoftForkDescPushBack(bip9_softforks, "coinbase", consensusParams, Consensus::DEPLOYMENT_COINBASE_ASSETS); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); diff --git a/src/validation.cpp b/src/validation.cpp index f615ae3ba7..89b8a26183 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2734,10 +2734,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); - if (block.vtx[0]->GetValueOut() > blockReward) + if (block.vtx[0]->GetValueOut(AreEnforcedValuesDeployed()) > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0]->GetValueOut(), blockReward), + block.vtx[0]->GetValueOut(AreEnforcedValuesDeployed()), blockReward), REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) @@ -5747,6 +5747,18 @@ bool AreEnforcedValuesDeployed() return fEnforcedValuesIsActive; } +bool AreCoinbaseCheckAssetsDeployed() +{ + if (fCheckCoinbaseAssetsIsActive) + return true; + + const ThresholdState thresholdState = VersionBitsTipState(GetParams().GetConsensus(), Consensus::DEPLOYMENT_COINBASE_ASSETS); + if (thresholdState == THRESHOLD_ACTIVE) + fCheckCoinbaseAssetsIsActive = true; + + return fCheckCoinbaseAssetsIsActive; +} + bool AreAssetsDeployed() { diff --git a/src/validation.h b/src/validation.h index f5ba96e7d1..ad24ab81f9 100644 --- a/src/validation.h +++ b/src/validation.h @@ -597,6 +597,8 @@ bool AreRestrictedAssetsDeployed(); bool AreEnforcedValuesDeployed(); +bool AreCoinbaseCheckAssetsDeployed(); + // Only used by test framework void SetEnforcedValues(bool value); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index f38bfecd04..6bbba82478 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -139,7 +139,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin } // calculate the old fee and fee-rate - nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(AreEnforcedValuesDeployed()); CFeeRate nOldFeeRate(nOldFee, txSize); CFeeRate nNewFeeRate; // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6b9730eeeb..36fa33ddde 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2252,7 +2252,7 @@ UniValue gettransaction(const JSONRPCRequest& request) CAmount nCredit = wtx.GetCredit(filter); CAmount nDebit = wtx.GetDebit(filter); CAmount nNet = nCredit - nDebit; - CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); + CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut(AreEnforcedValuesDeployed()) - nDebit : 0); entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); if (wtx.IsFromMe(filter)) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6143bfa27c..4b33b0691c 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1681,7 +1681,7 @@ void CWalletTx::GetAmounts(std::list& listReceived, CAmount nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { - CAmount nValueOut = tx->GetValueOut(); + CAmount nValueOut = tx->GetValueOut(AreEnforcedValuesDeployed()); nFee = nDebit - nValueOut; }