Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/confidential_validation.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

#include <chainparams.h>
#include <confidential_validation.h>
#include <issuance.h>
#include <pegins.h>
Expand Down Expand Up @@ -102,14 +103,13 @@ static bool VerifyIssuanceAmount(secp256k1_pedersen_commitment& value_commit, se

// Build value commitment
if (value.IsExplicit()) {
if (!MoneyRange(value.GetAmount()) || value.GetAmount() == 0) {
if ((asset == Params().GetConsensus().pegged_asset && !MoneyRange(value.GetAmount())) || value.GetAmount() <= 0) {
return false;
}
if (!rangeproof.empty()) {
return false;
}


ret = secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &value_commit, explicit_blinds, value.GetAmount(), &asset_gen);
// The explicit_blinds are all 0, and the amount is not 0. So secp256k1_pedersen_commit does not fail.
assert(ret == 1);
Expand Down
2 changes: 2 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ void SetupServerArgs(ArgsManager& argsman)

std::vector<std::string> elements_hidden_args = {"-con_fpowallowmindifficultyblocks", "-con_fpownoretargeting", "-con_nsubsidyhalvinginterval", "-con_bip16exception", "-con_bip34height", "-con_bip65height", "-con_bip66height", "-con_npowtargettimespan", "-con_npowtargetspacing", "-con_nrulechangeactivationthreshold", "-con_nminerconfirmationwindow", "-con_powlimit", "-con_bip34hash", "-con_nminimumchainwork", "-con_defaultassumevalid", "-npruneafterheight", "-fdefaultconsistencychecks", "-fmineblocksondemand", "-fallback_fee_enabled", "-pchmessagestart"};

argsman.AddArg("-acceptunlimitedissuances", strprintf("Relay and mine unblinded issuance transactions (default: %u)", DEFAULT_ACCEPT_UNLIMITED_ISSUANCES), ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
argsman.AddArg("-initialfreecoins", strprintf("The amount of OP_TRUE coins created in the genesis block. Primarily for testing. (default: %d)", 0), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-validatepegin", "Validate peg-in claims. An RPC connection will be attempted to the trusted mainchain daemon using the `mainchain*` settings below. All functionaries must run this enabled. (default: 1 if chain has federated peg)", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
argsman.AddArg("-mainchainrpchost=<host>", "The address which the daemon will try to connect to the trusted mainchain daemon to validate peg-ins, if enabled. (default: 127.0.0.1)", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
Expand Down Expand Up @@ -1076,6 +1077,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
fIsBareMultisigStd = args.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
nMaxDatacarrierBytes = args.GetIntArg("-datacarriersize", nMaxDatacarrierBytes);
fAcceptUnlimitedIssuances = args.GetBoolArg("-acceptunlimitedissuances", DEFAULT_ACCEPT_UNLIMITED_ISSUANCES);

// Option to startup with mocktime set (used for regression testing):
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
Expand Down
16 changes: 16 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,22 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
return true;
}

bool IsIssuanceInMoneyRange(const CTransaction& tx)
{
for (size_t i = 0; i < tx.vin.size(); ++i) {
const CAssetIssuance& issuance = tx.vin[i].assetIssuance;
if (issuance.IsNull()) {
continue;
}
if (issuance.nAmount.IsExplicit()) {
if (!MoneyRange(issuance.nAmount.GetAmount())) {
return false;
}
}
}
return true;
}

int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
{
return (std::max(nWeight, nSigOpCost * bytes_per_sigop) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
Expand Down
8 changes: 8 additions & 0 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_S
// ELEMENTS: keep a copy of the upstream default dust relay fee rate
static const unsigned int DUST_RELAY_TX_FEE_BITCOIN = 3000;

// ELEMENTS: allow unblinded issuances/reissuances greater than MAX_MONEY
static const bool DEFAULT_ACCEPT_UNLIMITED_ISSUANCES = true;

CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);

bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
Expand Down Expand Up @@ -124,6 +127,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
*/
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs);

/* ELEMENTS
* Check if blinded issuance/reissuance is greater than MAX_MONEY
*/
bool IsIssuanceInMoneyRange(const CTransaction& tx);

/** Compute the virtual transaction size (weight reinterpreted as bytes). */
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop);
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost, unsigned int bytes_per_sigop);
Expand Down
5 changes: 5 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ bool g_parallel_script_checks{false};
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
bool fAcceptUnlimitedIssuances = true;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;

uint256 hashAssumeValid;
Expand Down Expand Up @@ -722,6 +723,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
}
}

// Reject if unblinded issuance/reissuance is not in MoneyRange and acceptunlimitedissuances false
if (!fAcceptUnlimitedIssuances && !IsIssuanceInMoneyRange(tx))
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "issuance-out-of-range");

// Do not work on transactions that are too small.
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
// Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying
Expand Down
1 change: 1 addition & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ extern bool g_parallel_script_checks;
extern bool fRequireStandard;
extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern bool fAcceptUnlimitedIssuances;
/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
extern CFeeRate minRelayTxFee;
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
Expand Down
39 changes: 38 additions & 1 deletion test/functional/wallet_elements_21million.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)

class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = [['-blindedaddresses=1']] * self.num_nodes
args = [
"-blindedaddresses=1"
]
self.extra_args = [
args,
args,
args + ["-acceptunlimitedissuances=0"], # node 2 blocks unblinded issuances out of moneyrange
]

def setup_network(self, split=False):
self.setup_nodes()
Expand Down Expand Up @@ -42,6 +50,17 @@ def run_test(self):
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[asset], 200_000_000)

self.log.info("Issue more than 21 million of a unblinded non-policy asset")
issuance = self.nodes[0].issueasset(300_000_000, 100, False)
unblinded_asset = issuance['asset']
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[unblinded_asset], 300_000_000)

self.log.info("Reissue more than 21 million of a unblinded non-policy asset")
self.nodes[0].reissueasset(unblinded_asset, 200_000_000)
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getbalance()[unblinded_asset], 500_000_000)

# send more than 21 million of that asset
addr = self.nodes[1].getnewaddress()
self.nodes[0].sendtoaddress(address=addr, amount=22_000_000, assetlabel=asset)
Expand Down Expand Up @@ -90,5 +109,23 @@ def run_test(self):
self.nodes[2].loadwallet(self.default_wallet_name)
assert_equal(self.nodes[2].getbalance()[asset], 200_000_000)

# send some policy asset to node 2 for fees
addr = self.nodes[2].getnewaddress()
self.nodes[0].sendtoaddress(address=addr, amount=1)
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[2].getbalance()['bitcoin'], 1)

self.log.info("Issue more than 21 million of a non-policy asset on node 2 - rejected from mempool")
issuance = self.nodes[2].issueasset(300_000_000, 100, False)
issuance_tx = self.nodes[2].gettransaction(issuance["txid"])
assert_raises_rpc_error(-26, "issuance-out-of-range", self.nodes[2].sendrawtransaction, issuance_tx['hex'])
# transaction should be accepted on node 0
self.nodes[0].sendrawtransaction(issuance_tx["hex"])
asset = issuance['asset']
assert(issuance['txid'] in self.nodes[0].getrawmempool())
assert(issuance['txid'] not in self.nodes[2].getrawmempool())
self.generate(self.nodes[0], 1)
assert_equal(self.nodes[2].getbalance()[asset], 300_000_000)

if __name__ == '__main__':
WalletTest().main()