diff --git a/configure.ac b/configure.ac index cdf5481325..0cae531ffc 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 18) define(_CLIENT_VERSION_REVISION, 1) -define(_CLIENT_VERSION_BUILD, 5) +define(_CLIENT_VERSION_BUILD, 6) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2019) diff --git a/src/blind.cpp b/src/blind.cpp index 8c0ec2cf0b..446fb447f3 100644 --- a/src/blind.cpp +++ b/src/blind.cpp @@ -180,8 +180,11 @@ bool GenerateRangeproof(std::vector& rangeproof, const std::vecto memcpy(asset_message+32, asset_blindptrs[asset_blindptrs.size()-1], 32); // Sign rangeproof + int ct_exponent = std::min(std::max((int)gArgs.GetArg("-ct_exponent", 0), -1), 18); + int ct_bits = (int)gArgs.GetArg("-ct_bits", 52); // If min_value is 0, scriptPubKey must be unspendable - int res = secp256k1_rangeproof_sign(secp256k1_blind_context, rangeproof.data(), &nRangeProofLen, scriptPubKey.IsUnspendable() ? 0 : 1, &value_commit, value_blindptrs.back(), nonce.begin(), std::min(std::max((int)gArgs.GetArg("-ct_exponent", 0), -1),18), std::min(std::max((int)gArgs.GetArg("-ct_bits", 36), 1), 51), amount, asset_message, sizeof(asset_message), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &gen); + uint64_t min_value = scriptPubKey.IsUnspendable() ? 0 : 1; + int res = secp256k1_rangeproof_sign(secp256k1_blind_context, rangeproof.data(), &nRangeProofLen, min_value, &value_commit, value_blindptrs.back(), nonce.begin(), ct_exponent, ct_bits, amount, asset_message, sizeof(asset_message), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &gen); rangeproof.resize(nRangeProofLen); return (res == 1); } diff --git a/src/blind.h b/src/blind.h index 4296b70ce7..0b668a7b3d 100644 --- a/src/blind.h +++ b/src/blind.h @@ -14,8 +14,8 @@ #include #include -//! ELEMENTS: 36-bit rangeproof size -static const size_t DEFAULT_RANGEPROOF_SIZE = 2893; +//! ELEMENTS: 52-bit rangeproof size +static const size_t DEFAULT_RANGEPROOF_SIZE = 4174; /* * Unblind a pair of confidential asset and value. diff --git a/src/init.cpp b/src/init.cpp index 941ac9bd83..1db635e00f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -591,7 +591,7 @@ void SetupServerArgs() gArgs.AddArg("-feeasset=", strprintf("Asset ID (hex) for mempool/relay fees (default: %s)", defaultChainParams->GetConsensus().pegged_asset.GetHex()), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-subsidyasset=", strprintf("Asset ID (hex) for the block subsidy (default: %s)", defaultChainParams->GetConsensus().pegged_asset.GetHex()), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-initialreissuancetokens=", "The amount of reissuance tokens created in the genesis block. (default: 0)", false, OptionsCategory::CHAINPARAMS); - gArgs.AddArg("-ct_bits", strprintf("The default number of hiding bits in a rangeproof. Will be exceeded to cover amounts exceeding the maximum hiding value. (default: %d)", 36), false, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-ct_bits", strprintf("The default number of hiding bits in a rangeproof. Will be exceeded to cover amounts exceeding the maximum hiding value. (default: %d)", 52), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-ct_exponent", strprintf("The hiding exponent. (default: %s)", 0), false, OptionsCategory::CHAINPARAMS); // Add the hidden options @@ -1877,7 +1877,6 @@ bool AppInitMain(InitInterfaces& interfaces) SetRPCWarmupFinished(); // ELEMENTS: - uiInterface.InitMessage(_("Awaiting mainchain RPC warmup")); if (!MainchainRPCCheck(true)) { //Initial check only const std::string err_msg = "ERROR: elements is set to verify pegins but cannot get valid response from the mainchain daemon. Please check debug.log for more information.\n\nIf you haven't setup a bitcoind please get the latest stable version from https://bitcoincore.org/en/download/ or if you do not need to validate pegins set in your elements configuration validatepegin=0"; // We fail immediately if this node has RPC server enabled diff --git a/src/test/blind_tests.cpp b/src/test/blind_tests.cpp index e3cf57e8f7..b3f8f27487 100644 --- a/src/test/blind_tests.cpp +++ b/src/test/blind_tests.cpp @@ -265,17 +265,17 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test) // Check wallet borromean-based rangeproof results against expected args size_t proof_size = DEFAULT_RANGEPROOF_SIZE; - BOOST_CHECK(tx4.witness.vtxoutwit[2].vchRangeproof.size() == proof_size); + BOOST_CHECK_EQUAL(tx4.witness.vtxoutwit[2].vchRangeproof.size(), proof_size); secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); int exp = 0; int mantissa = 0; uint64_t min_value = 0; uint64_t max_value = 0; BOOST_CHECK(secp256k1_rangeproof_info(ctx, &exp, &mantissa, &min_value, &max_value, tx4.witness.vtxoutwit[2].vchRangeproof.data(), proof_size) == 1); - BOOST_CHECK(exp == 0); - BOOST_CHECK(mantissa == 36); // 36 bit default - BOOST_CHECK(min_value == 1); - BOOST_CHECK(max_value == 68719476736); + BOOST_CHECK_EQUAL(exp, 0); + BOOST_CHECK_EQUAL(mantissa, 52); // 36 bit default + BOOST_CHECK_EQUAL(min_value, 1); + BOOST_CHECK_EQUAL(max_value, 4503599627370496); } { inputs.clear(); diff --git a/src/validation.cpp b/src/validation.cpp index 3c5694e3d9..aa890b4822 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5288,6 +5288,7 @@ bool MainchainRPCCheck(const bool init) // Next, check for working and valid rpc if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) { + uiInterface.InitMessage(_("Awaiting mainchain RPC warmup")); // During init try until a non-RPC_IN_WARMUP result while (true) { try { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index baa18f701f..c5aa169397 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4984,6 +4984,19 @@ UniValue initpegoutwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "bitcoin_descriptor must be a ranged descriptor."); } + // Check if we can actually generate addresses(catches hardened derivation steps etc) before + // writing to cache + UniValue address_list(UniValue::VARR); + for (int i = counter; i < counter+3; i++) { + std::vector scripts; + if (!desc->Expand(i, provider, scripts, provider)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Could not generate lookahead addresses with descriptor. Are there hardened derivations after the xpub?"); + } + CTxDestination destination; + ExtractDestination(scripts[0], destination); + address_list.push_back(EncodeParentDestination(destination)); + } + // For our manual pattern matching, we don't want the checksum part. auto checksum_char = bitcoin_desc.find('#'); if (checksum_char != std::string::npos) { @@ -5052,16 +5065,6 @@ UniValue initpegoutwallet(const JSONRPCRequest& request) assert(len == 33); assert(negatedpubkeybytes.size() == 33); - UniValue address_list(UniValue::VARR); - for (int i = counter; i < counter+3; i++) { - std::vector scripts; - if (!desc->Expand(i, provider, scripts, provider)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Could not generate lookahead addresses with descriptor. This is a bug."); - } - CTxDestination destination; - ExtractDestination(scripts[0], destination); - address_list.push_back(EncodeParentDestination(destination)); - } UniValue pak(UniValue::VOBJ); pak.pushKV("pakentry", "pak=" + HexStr(negatedpubkeybytes) + ":" + HexStr(online_pubkey)); pak.pushKV("liquid_pak", HexStr(online_pubkey)); @@ -5418,6 +5421,12 @@ unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgr template static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef, T_tx& tx_aux, T_merkle_block& merkleBlock) { + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; + if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( RPCHelpMan{"createrawpegin", @@ -5441,9 +5450,6 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef }, }.ToString()); - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - auto locked_chain = pwallet->chain().lock(); LOCK(pwallet->cs_wallet); @@ -5640,6 +5646,11 @@ UniValue createrawpegin(const JSONRPCRequest& request) UniValue claimpegin(const JSONRPCRequest& request) { + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) + return NullUniValue; if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) throw std::runtime_error( @@ -5661,8 +5672,6 @@ UniValue claimpegin(const JSONRPCRequest& request) }, }.ToString()); - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); CTransactionRef tx_ref; CMutableTransaction mtx; @@ -5673,8 +5682,17 @@ UniValue claimpegin(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Peg-ins cannot be completed during initial sync or reindexing."); } + // NOTE: Making an RPC from within another RPC is not generally a good idea. In particular, it + // is necessary to copy the URI, which contains the wallet if one was given; otherwise + // multi-wallet support will silently break. The resulting request object is still missing a + // bunch of other fields, although they are usually not used by RPC handlers. This is a + // brittle hack, and further examples of this pattern should not be introduced. + // Get raw peg-in transaction - UniValue ret(createrawpegin(request)); + JSONRPCRequest req; + req.URI = request.URI; + req.params = request.params; + UniValue ret(createrawpegin(req)); // See the note above, on why this is a bad idea. // Make sure it can be propagated and confirmed if (!ret["mature"].isNull() && ret["mature"].get_bool() == false) { @@ -5682,11 +5700,12 @@ UniValue claimpegin(const JSONRPCRequest& request) } // Sign it - JSONRPCRequest request2; + JSONRPCRequest req2; + req2.URI = request.URI; UniValue varr(UniValue::VARR); varr.push_back(ret["hex"]); - request2.params = varr; - UniValue result = signrawtransactionwithwallet(request2); + req2.params = varr; + UniValue result = signrawtransactionwithwallet(req2); // See the note above, on why this is a bad idea. if (!DecodeHexTx(mtx, result["hex"].get_str(), false, true)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); @@ -6168,7 +6187,7 @@ UniValue issueasset(const JSONRPCRequest& request) if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - asset_dest = PKHash(newKey.GetID()); + asset_dest = WitnessV0KeyHash(newKey.GetID()); pwallet->SetAddressBook(asset_dest, "", "receive"); asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest)); } @@ -6176,7 +6195,7 @@ UniValue issueasset(const JSONRPCRequest& request) if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - token_dest = PKHash(newKey.GetID()); + token_dest = WitnessV0KeyHash(newKey.GetID()); pwallet->SetAddressBook(token_dest, "", "receive"); token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest)); } @@ -6275,7 +6294,7 @@ UniValue reissueasset(const JSONRPCRequest& request) if (!pwallet->GetKeyFromPool(newAssetKey)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - CTxDestination asset_dest = PKHash(newAssetKey.GetID()); + CTxDestination asset_dest = WitnessV0KeyHash(newAssetKey.GetID()); pwallet->SetAddressBook(asset_dest, "", "receive"); CPubKey asset_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(asset_dest)); @@ -6284,7 +6303,7 @@ UniValue reissueasset(const JSONRPCRequest& request) if (!pwallet->GetKeyFromPool(newTokenKey)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - CTxDestination token_dest = PKHash(newTokenKey.GetID()); + CTxDestination token_dest = WitnessV0KeyHash(newTokenKey.GetID()); pwallet->SetAddressBook(token_dest, "", "receive"); CPubKey token_dest_blindpub = pwallet->GetBlindingPubKey(GetScriptForDestination(token_dest)); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 837540ef0a..6e01a00178 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -74,11 +74,11 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; //! -paytxfee default constexpr CAmount DEFAULT_PAY_TX_FEE = 0; //! -fallbackfee default -static const CAmount DEFAULT_FALLBACK_FEE = 20000; +static const CAmount DEFAULT_FALLBACK_FEE = 2000; //! -discardfee default static const CAmount DEFAULT_DISCARD_FEE = 10000; //! -mintxfee default -static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; +static const CAmount DEFAULT_TRANSACTION_MINFEE = 100; //! minimum recommended increment for BIP 125 replacement txs static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000; //! Default for -spendzeroconfchange diff --git a/test/functional/feature_confidential_transactions.py b/test/functional/feature_confidential_transactions.py index 70ee02a633..3ff38afecb 100755 --- a/test/functional/feature_confidential_transactions.py +++ b/test/functional/feature_confidential_transactions.py @@ -361,7 +361,9 @@ def run_test(self): # Send some bitcoin and other assets over as well to fund wallet addr = self.nodes[2].getnewaddress() - self.nodes[0].sendtoaddress(addr, 5) + txid = self.nodes[0].sendtoaddress(addr, 5) + # Make sure we're doing 52 bits of hiding which covers 21M BTC worth + assert_equal(self.nodes[0].getrawtransaction(txid, 1)["vout"][0]["ct-bits"], 52) self.nodes[0].sendmany("", {addr: 1, self.nodes[2].getnewaddress(): 13}, 0, "", [], False, 1, "UNSET", {addr: test_asset}) self.sync_all() diff --git a/test/functional/feature_fedpeg.py b/test/functional/feature_fedpeg.py index 4dc44788a8..6b10ef54cd 100755 --- a/test/functional/feature_fedpeg.py +++ b/test/functional/feature_fedpeg.py @@ -225,6 +225,14 @@ def run_test(self): raw = parent.gettransaction(txid1)["hex"] + # Create a wallet in order to test that multi-wallet support works correctly for claimpegin + # (Regression test for https://github.com/ElementsProject/elements/issues/812 .) + sidechain.createwallet("throwaway") + # Set up our sidechain RPCs to use the first wallet (with empty name). We do this by + # overriding the RPC object in a hacky way, to avoid breaking a different hack on TestNode + # that enables generate() to work despite the deprecation of the generate RPC. + sidechain.rpc = sidechain.get_wallet_rpc("") + print("Attempting peg-ins") # First attempt fails the consensus check but gives useful result try: diff --git a/test/functional/feature_issuance.py b/test/functional/feature_issuance.py index 661006f415..57add7a003 100755 --- a/test/functional/feature_issuance.py +++ b/test/functional/feature_issuance.py @@ -16,7 +16,7 @@ def process_raw_issuance(node, issuance_list): raise Exception('Issuance list too long') # Make enough outputs for any subsequent spend next_destinations = {} - output_values = (node.getbalance()['bitcoin']-1)/5 + output_values = Decimal('%.8f' % ((node.getbalance()['bitcoin']-1)/5)) for i in range(5): next_destinations[node.getnewaddress()] = output_values @@ -382,7 +382,7 @@ def run_test(self): assert_equal(blinded_multisig, self.nodes[0].getaddressinfo(utxo_info["address"])["confidential"]) break assert(utxo_info is not None) - assert(utxo_info["amountblinder"] is not "0000000000000000000000000000000000000000000000000000000000000000") + assert(utxo_info["amountblinder"] != "0000000000000000000000000000000000000000000000000000000000000000") # Now make transaction spending that input raw_tx = self.nodes[0].createrawtransaction([], {issued_address:1}, 0, False, {issued_address:issued_asset["token"]})