Skip to content

Commit a3d55ee

Browse files
authored
Merge pull request #1388 from psgreco/elem-23.3.0-rc2
Prepare 23.3.0rc2
2 parents 1611d00 + 3a3d77e commit a3d55ee

File tree

13 files changed

+135
-27
lines changed

13 files changed

+135
-27
lines changed

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ AC_PREREQ([2.69])
22
define(_CLIENT_VERSION_MAJOR, 23)
33
define(_CLIENT_VERSION_MINOR, 3)
44
define(_CLIENT_VERSION_BUILD, 0)
5-
define(_CLIENT_VERSION_RC, 1)
5+
define(_CLIENT_VERSION_RC, 2)
66
define(_CLIENT_VERSION_IS_RELEASE, true)
77
define(_COPYRIGHT_YEAR, 2024)
88
define(_COPYRIGHT_HOLDERS,[The %s developers])

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
@@ -3050,7 +3050,7 @@ static RPCHelpMan rawissueasset()
30503050
CAmount asset_amount = 0;
30513051
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
30523052
if (asset_amount_uni.isNum()) {
3053-
asset_amount = AmountFromValue(asset_amount_uni);
3053+
asset_amount = AmountFromValue(asset_amount_uni, false);
30543054
if (asset_amount <= 0) {
30553055
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
30563056
}
@@ -3067,7 +3067,7 @@ static RPCHelpMan rawissueasset()
30673067
CAmount token_amount = 0;
30683068
const UniValue& token_amount_uni = issuance_o["token_amount"];
30693069
if (token_amount_uni.isNum()) {
3070-
token_amount = AmountFromValue(token_amount_uni);
3070+
token_amount = AmountFromValue(token_amount_uni, false);
30713071
if (token_amount <= 0) {
30723072
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, token_amount must be positive");
30733073
}
@@ -3178,7 +3178,7 @@ static RPCHelpMan rawreissueasset()
31783178
CAmount asset_amount = 0;
31793179
const UniValue& asset_amount_uni = issuance_o["asset_amount"];
31803180
if (asset_amount_uni.isNum()) {
3181-
asset_amount = AmountFromValue(asset_amount_uni);
3181+
asset_amount = AmountFromValue(asset_amount_uni, false);
31823182
if (asset_amount <= 0) {
31833183
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, asset_amount must be positive");
31843184
}

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
@@ -1430,7 +1430,7 @@ CAmountMap CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter)
14301430
for (const CTxIn& txin : tx.vin)
14311431
{
14321432
nDebit += GetDebit(txin, filter);
1433-
if (!MoneyRange(nDebit))
1433+
if (!MoneyRange(nDebit, Params().GetConsensus().pegged_asset))
14341434
throw std::runtime_error(std::string(__func__) + ": value out of range");
14351435
}
14361436
return nDebit;

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
'rpc_getnewblockhex.py',
113113
'wallet_elements_regression_1172.py --legacy-wallet',
114114
'wallet_elements_regression_1259.py --legacy-wallet',
115+
'wallet_elements_21million.py',
115116
'feature_trim_headers.py',
116117
# Longest test should go first, to favor running tests in parallel
117118
'wallet_hd.py --legacy-wallet',
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2017-2020 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
from test_framework.blocktools import COINBASE_MATURITY
7+
from test_framework.test_framework import BitcoinTestFramework
8+
from test_framework.util import (
9+
assert_equal,
10+
)
11+
12+
class WalletTest(BitcoinTestFramework):
13+
def set_test_params(self):
14+
self.setup_clean_chain = True
15+
self.num_nodes = 3
16+
self.extra_args = [['-blindedaddresses=1']] * self.num_nodes
17+
18+
def setup_network(self, split=False):
19+
self.setup_nodes()
20+
self.connect_nodes(0, 1)
21+
self.connect_nodes(1, 2)
22+
self.connect_nodes(0, 2)
23+
self.sync_all()
24+
25+
def skip_test_if_missing_module(self):
26+
self.skip_if_no_wallet()
27+
28+
def run_test(self):
29+
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
30+
31+
assert_equal(self.nodes[0].getbalance(), {'bitcoin': 50})
32+
assert_equal(self.nodes[1].getbalance(), {'bitcoin': 0})
33+
34+
self.log.info("Issue more than 21 million of a non-policy asset")
35+
issuance = self.nodes[0].issueasset(100_000_000, 100)
36+
asset = issuance['asset']
37+
self.generate(self.nodes[0], 1)
38+
assert_equal(self.nodes[0].getbalance()[asset], 100_000_000)
39+
40+
self.log.info("Reissue more than 21 million of a non-policy asset")
41+
self.nodes[0].reissueasset(asset, 100_000_000)
42+
self.generate(self.nodes[0], 1)
43+
assert_equal(self.nodes[0].getbalance()[asset], 200_000_000)
44+
45+
# send more than 21 million of that asset
46+
addr = self.nodes[1].getnewaddress()
47+
self.nodes[0].sendtoaddress(address=addr, amount=22_000_000, assetlabel=asset)
48+
self.generate(self.nodes[0], 1)
49+
assert_equal(self.nodes[0].getbalance()[asset], 178_000_000)
50+
assert_equal(self.nodes[1].getbalance()[asset], 22_000_000)
51+
52+
# unload/load wallet
53+
self.nodes[1].unloadwallet("")
54+
self.nodes[1].loadwallet("")
55+
assert_equal(self.nodes[1].getbalance()[asset], 22_000_000)
56+
57+
# send more than 45 million of that asset
58+
addr = self.nodes[2].getnewaddress()
59+
self.nodes[0].sendtoaddress(address=addr, amount=46_000_000, assetlabel=asset)
60+
self.generate(self.nodes[0], 1)
61+
assert_equal(self.nodes[0].getbalance()[asset], 132_000_000)
62+
assert_equal(self.nodes[2].getbalance()[asset], 46_000_000)
63+
64+
# unload/load wallet
65+
self.nodes[2].unloadwallet("")
66+
self.nodes[2].loadwallet("")
67+
assert_equal(self.nodes[2].getbalance()[asset], 46_000_000)
68+
69+
# send some policy asset to node 1 for fees
70+
addr = self.nodes[1].getnewaddress()
71+
self.nodes[0].sendtoaddress(address=addr, amount=1)
72+
self.generate(self.nodes[0], 1)
73+
assert_equal(self.nodes[1].getbalance()['bitcoin'], 1)
74+
assert_equal(self.nodes[1].getbalance()[asset], 22_000_000)
75+
76+
# send the remainders
77+
addr = self.nodes[2].getnewaddress()
78+
self.nodes[0].sendtoaddress(address=addr, amount=132_000_000, assetlabel=asset)
79+
addr = self.nodes[2].getnewaddress()
80+
self.nodes[1].sendtoaddress(address=addr, amount=22_000_000, assetlabel=asset)
81+
self.sync_mempools()
82+
self.generate(self.nodes[0], 1)
83+
84+
assert asset not in self.nodes[0].getbalance()
85+
assert asset not in self.nodes[1].getbalance()
86+
assert_equal(self.nodes[2].getbalance()[asset], 200_000_000)
87+
88+
# unload/load wallet
89+
self.nodes[2].unloadwallet("")
90+
self.nodes[2].loadwallet("")
91+
assert_equal(self.nodes[2].getbalance()[asset], 200_000_000)
92+
93+
if __name__ == '__main__':
94+
WalletTest().main()

0 commit comments

Comments
 (0)