Skip to content

Commit e9a9144

Browse files
authored
Merge pull request #1385 from delta1/wallet-receive-gt-21million
assets: allow issuance and transaction amounts of more than MAX_MONEY for non-policy assets
2 parents cc2ecbb + e7f32aa commit e9a9144

12 files changed

Lines changed: 134 additions & 26 deletions

File tree

src/asset.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,19 @@ bool operator==(const CAmountMap& a, const CAmountMap& b);
9090
bool operator!=(const CAmountMap& a, const CAmountMap& b);
9191
bool operator!(const CAmountMap& a); // Check if all values are 0
9292

93-
inline bool MoneyRange(const CAmountMap& mapValue) {
93+
inline bool MoneyRange(const CAmountMap& mapValue, const CAsset& pegged_asset) {
9494
for(CAmountMap::const_iterator it = mapValue.begin(); it != mapValue.end(); it++) {
95-
if (it->second < 0 || it->second > MAX_MONEY) {
95+
if (it->second < 0 || ((pegged_asset.IsNull() || it->first == pegged_asset) && it->second > MAX_MONEY)) {
9696
return false;
9797
}
9898
}
9999
return true;
100100
}
101101

102+
inline bool MoneyRange(const CAmountMap& mapValue) {
103+
return MoneyRange(mapValue, CAsset());
104+
}
105+
102106
CAmount valueFor(const CAmountMap& mapValue, const CAsset& asset);
103107

104108
std::ostream& operator<<(std::ostream& out, const CAmountMap& map);

src/blind.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <blind.h>
6+
#include <chainparams.h>
67

78
#include <hash.h>
89
#include <primitives/transaction.h>
@@ -157,11 +158,6 @@ bool UnblindConfidentialPair(const CKey& blinding_key, const CConfidentialValue&
157158
return false;
158159
}
159160

160-
// Value sidechannel must be a transaction-valid amount (should be belt-and-suspenders check)
161-
if (amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
162-
return false;
163-
}
164-
165161
// Convenience pointers to starting point of each recovered 32 byte message
166162
unsigned char *asset_type = msg;
167163
unsigned char *asset_blinder = msg+32;
@@ -172,6 +168,13 @@ bool UnblindConfidentialPair(const CKey& blinding_key, const CConfidentialValue&
172168
return false;
173169
}
174170

171+
CAsset asset{std::vector<unsigned char>{asset_type, asset_type + 32}};
172+
173+
// Value sidechannel must be a transaction-valid amount (should be belt-and-suspenders check)
174+
if ((!committedScript.IsUnspendable() && amount == 0) || (asset == Params().GetConsensus().pegged_asset && (amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)))) {
175+
return false;
176+
}
177+
175178
// Serialize both generators then compare
176179
unsigned char observed_generator[33];
177180
unsigned char derived_generator[33];
@@ -182,7 +185,7 @@ bool UnblindConfidentialPair(const CKey& blinding_key, const CConfidentialValue&
182185
}
183186

184187
amount_out = (CAmount)amount;
185-
asset_out = CAsset(std::vector<unsigned char>(asset_type, asset_type+32));
188+
asset_out = asset;
186189
asset_blinding_factor_out = uint256(std::vector<unsigned char>(asset_blinder, asset_blinder+32));
187190
return true;
188191
}

src/rpc/rawtransaction.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,7 +3049,7 @@ static RPCHelpMan rawissueasset()
30493049
CAmount asset_amount = 0;
30503050
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
30513051
if (asset_amount_uni.isNum()) {
3052-
asset_amount = AmountFromValue(asset_amount_uni);
3052+
asset_amount = AmountFromValue(asset_amount_uni, false);
30533053
if (asset_amount <= 0) {
30543054
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
30553055
}
@@ -3066,7 +3066,7 @@ static RPCHelpMan rawissueasset()
30663066
CAmount token_amount = 0;
30673067
const UniValue& token_amount_uni = issuance_o["token_amount"];
30683068
if (token_amount_uni.isNum()) {
3069-
token_amount = AmountFromValue(token_amount_uni);
3069+
token_amount = AmountFromValue(token_amount_uni, false);
30703070
if (token_amount <= 0) {
30713071
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, token_amount must be positive");
30723072
}
@@ -3177,7 +3177,7 @@ static RPCHelpMan rawreissueasset()
31773177
CAmount asset_amount = 0;
31783178
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
31793179
if (asset_amount_uni.isNum()) {
3180-
asset_amount = AmountFromValue(asset_amount_uni);
3180+
asset_amount = AmountFromValue(asset_amount_uni, false);
31813181
if (asset_amount <= 0) {
31823182
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
31833183
}

src/rpc/util.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,14 @@ void RPCTypeCheckObj(const UniValue& o,
9090
}
9191
}
9292

93-
CAmount AmountFromValue(const UniValue& value, int decimals)
93+
CAmount AmountFromValue(const UniValue& value, bool check_range, int decimals)
9494
{
9595
if (!value.isNum() && !value.isStr())
9696
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
9797
CAmount amount;
9898
if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
9999
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
100-
if (!MoneyRange(amount))
100+
if (amount < 0 || (check_range && !MoneyRange(amount)))
101101
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
102102
return amount;
103103
}

src/rpc/util.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
9191
* @param[in] decimals Number of significant digits (default: 8).
9292
* @returns a CAmount if the various checks pass.
9393
*/
94-
CAmount AmountFromValue(const UniValue& value, int decimals = 8);
94+
CAmount AmountFromValue(const UniValue& value, bool check_range = true, int decimals = 8);
9595

9696
using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
9797
std::string HelpExampleCli(const std::string& methodname, const std::string& args);

src/wallet/receive.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,17 @@ CAmountMap TxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const ismine
7070
{
7171
LOCK(wallet.cs_wallet);
7272

73+
CAsset pegged_asset{Params().GetConsensus().pegged_asset};
7374
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
7475
if (wallet.IsMine(wtx.tx->vout[i]) & filter) {
76+
CAsset asset{wtx.GetOutputAsset(wallet, i)};
7577
CAmount credit = std::max<CAmount>(0, wtx.GetOutputValueOut(wallet, i));
76-
if (!MoneyRange(credit))
78+
if (asset == pegged_asset && !MoneyRange(credit)) {
7779
throw std::runtime_error(std::string(__func__) + ": value out of range");
80+
}
7881

79-
nCredit[wtx.GetOutputAsset(wallet, i)] += credit;
80-
if (!MoneyRange(nCredit))
82+
nCredit[asset] += credit;
83+
if (!MoneyRange(nCredit, pegged_asset))
8184
throw std::runtime_error(std::string(__func__) + ": value out of range");
8285
}
8386
}
@@ -226,16 +229,18 @@ CAmountMap CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wt
226229
bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
227230
CAmountMap nCredit;
228231
uint256 hashTx = wtx.GetHash();
232+
CAsset pegged_asset{Params().GetConsensus().pegged_asset};
229233
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
230234
{
231235
if (!wallet.IsSpent(hashTx, i) && (allow_used_addresses || !wallet.IsSpentKey(hashTx, i))) {
232236
if (wallet.IsMine(wtx.tx->vout[i]) & filter) {
237+
CAsset asset = wtx.GetOutputAsset(wallet, i);
233238
CAmount credit = std::max<CAmount>(0, wtx.GetOutputValueOut(wallet, i));
234-
if (!MoneyRange(credit))
239+
if (asset == pegged_asset && !MoneyRange(credit))
235240
throw std::runtime_error(std::string(__func__) + ": value out of range");
236241

237242
nCredit[wtx.GetOutputAsset(wallet, i)] += std::max<CAmount>(0, wtx.GetOutputValueOut(wallet, i));
238-
if (!MoneyRange(nCredit))
243+
if (!MoneyRange(nCredit, pegged_asset))
239244
throw std::runtime_error(std::string(__func__) + ": value out of range");
240245
}
241246
}

src/wallet/rpc/elements.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,8 +1426,8 @@ RPCHelpMan issueasset()
14261426
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance can only be done on elements-style chains. Note: `-regtest` is Bitcoin's regtest mode, instead try `-chain=<custom chain name>`");
14271427
}
14281428

1429-
CAmount nAmount = AmountFromValue(request.params[0]);
1430-
CAmount nTokens = AmountFromValue(request.params[1]);
1429+
CAmount nAmount = AmountFromValue(request.params[0], false);
1430+
CAmount nTokens = AmountFromValue(request.params[1], false);
14311431
if (nAmount == 0 && nTokens == 0) {
14321432
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
14331433
}
@@ -1524,7 +1524,7 @@ RPCHelpMan reissueasset()
15241524
std::string assetstr = request.params[0].get_str();
15251525
CAsset asset = GetAssetFromString(assetstr);
15261526

1527-
CAmount nAmount = AmountFromValue(request.params[1]);
1527+
CAmount nAmount = AmountFromValue(request.params[1], false);
15281528
if (nAmount <= 0) {
15291529
throw JSONRPCError(RPC_TYPE_ERROR, "Reissuance must create a non-zero amount.");
15301530
}

src/wallet/rpc/spend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static void ParseRecipients(const UniValue& address_amounts, const UniValue& add
5050
destinations.insert(dest);
5151

5252
CScript script_pub_key = GetScriptForDestination(dest);
53-
CAmount amount = AmountFromValue(address_amounts[i++]);
53+
CAmount amount = AmountFromValue(address_amounts[i++], asset == Params().GetConsensus().pegged_asset);
5454

5555
bool subtract_fee = false;
5656
for (unsigned int idx = 0; idx < subtract_fee_outputs.size(); idx++) {
@@ -124,7 +124,7 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un
124124
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
125125
}
126126
// Fee rates in sat/vB cannot represent more than 3 significant digits.
127-
cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /* decimals */ 3)};
127+
cc.m_feerate = CFeeRate{AmountFromValue(fee_rate, /*check_range=*/true, /*decimals=*/3)};
128128
if (override_min_fee) cc.fOverrideFeeRate = true;
129129
// Default RBF to true for explicit fee_rate, if unset.
130130
if (!cc.m_signal_bip125_rbf) cc.m_signal_bip125_rbf = true;

src/wallet/spend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,9 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput> &vCoins, const C
265265
if (asset_filter && asset != *asset_filter) {
266266
continue;
267267
}
268-
if (outValue < nMinimumAmount || outValue > nMaximumAmount)
268+
if (outValue < nMinimumAmount || (asset == Params().GetConsensus().pegged_asset && outValue > nMaximumAmount)) {
269269
continue;
270+
}
270271

271272
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
272273
continue;

src/wallet/wallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ CAmountMap CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter)
14181418
for (const CTxIn& txin : tx.vin)
14191419
{
14201420
nDebit += GetDebit(txin, filter);
1421-
if (!MoneyRange(nDebit))
1421+
if (!MoneyRange(nDebit, Params().GetConsensus().pegged_asset))
14221422
throw std::runtime_error(std::string(__func__) + ": value out of range");
14231423
}
14241424
return nDebit;

0 commit comments

Comments
 (0)