Skip to content

Commit

Permalink
post-rebase fixups
Browse files Browse the repository at this point in the history
in no particular order

* likely overkill, but works for now until this gets migrated to a descriptor wallet.
* bip352: skip if coins map is empty
* dont try to calculate the public data if the spent_coins map is empty. this
  can occure when SyncTransaction is called for a variety of reasons (outside of
  scanning a block or a tx being added to the mempool).
* rpc: silentpayments rescanblockchain
  we dont need the wallet to be unlocked for silent payments
  so skip the ensure check when rescanning.
  note, need to think through how to handle this when the wallet
  has multiple descriptors in the same wallet since some of the
  descriptors may require the wallet to be unlocked.
* fix encryption
* update tests
  • Loading branch information
josibake committed May 4, 2024
1 parent 00b30f0 commit 29ae0a0
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 6 deletions.
1 change: 0 additions & 1 deletion src/test/bip352_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ BOOST_AUTO_TEST_CASE(bip352_send_and_receive_test_vectors)
std::string pubkey_hex = output["pub_key"].get_str();
expected_outputs.emplace_back(ParseHex(pubkey_hex));
}
BOOST_TEST_MESSAGE(found_outputs->size());
BOOST_CHECK(found_outputs->size() == expected_outputs.size());
for (const auto& output : *found_outputs) {
BOOST_CHECK(std::find(expected_outputs.begin(), expected_outputs.end(), output.output) != expected_outputs.end());
Expand Down
1 change: 1 addition & 0 deletions src/util/bip352.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ std::optional<PubTweakData> GetSilentPaymentTweakDataFromTxInputs(const std::vec
std::vector<CPubKey> pubkeys;
std::vector<XOnlyPubKey> xonly_pubkeys;
std::vector<COutPoint> tx_outpoints;
if (coins.empty()) return std::nullopt;
for (const CTxIn& txin : vin) {
const Coin& coin = coins.at(txin.prevout);
Assert(!coin.IsSpent());
Expand Down
4 changes: 3 additions & 1 deletion src/wallet/rpc/transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,9 @@ RPCHelpMan rescanblockchain()
LOCK(pwallet->m_relock_mutex);
{
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(*pwallet);
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_SILENT_PAYMENTS)) {
EnsureWalletIsUnlocked(*pwallet);
}
int tip_height = pwallet->GetLastBlockHeight();

if (!request.params[0].isNull()) {
Expand Down
15 changes: 11 additions & 4 deletions src/wallet/silentpayments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ SilentPaymentsSPKM::SilentPaymentsSPKM(WalletStorage& storage, int64_t labels_si
}
m_address.m_spend_pubkey = derived.key.GetPubKey();
if (m_storage.HasEncryptionKeys()) {
std::vector<unsigned char> crypted_spend_key;
CKeyingMaterial secret{UCharCast(derived.key.begin()), UCharCast(derived.key.end())};
std::vector<unsigned char> crypted_spend_key = m_spend_crypted_key;
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { return EncryptSecret(encryption_key, secret, derived.key.GetPubKey().GetHash(), crypted_spend_key);})) {
throw std::runtime_error("Unable to encrypt silent payments spend key");
}
m_spend_crypted_key = crypted_spend_key;
} else {
m_spend_key = derived.key;
}
Expand Down Expand Up @@ -151,8 +152,9 @@ bool SilentPaymentsSPKM::CheckDecryptionKey(const CKeyingMaterial& master_key)
assert(!m_spend_key.IsValid());

CKey key;
if (!DecryptKey(master_key, m_spend_crypted_key, m_address.m_spend_pubkey, key)) {
LogPrintf("The wallet is probably corrupted: Unable to decrypt silent payments spend key");
std::vector<unsigned char> crypted_secret = m_spend_crypted_key;
CPubKey spend_pubkey = m_address.m_spend_pubkey;
if (!DecryptKey(master_key, crypted_secret, spend_pubkey, key)) {
throw std::runtime_error("Error unlocking wallet: unable to decrypt silent payments spend key. Your wallet file may be corrupt.");
}
return true;
Expand Down Expand Up @@ -190,7 +192,12 @@ util::Result<CTxDestination> SilentPaymentsSPKM::GetReservedDestination(const Ou

bool SilentPaymentsSPKM::TopUp(unsigned int size)
{
// Nothing to do here
LOCK(cs_sp_man);
std::set<CScript> new_spks;
for (const auto& spk : m_spk_tweaks) {
new_spks.emplace(spk.first);
}
m_storage.TopUpCallback(new_spks, this);
return true;
}

Expand Down
39 changes: 39 additions & 0 deletions test/functional/wallet_silentpayments_receiving.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,43 @@ def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
self.skip_if_no_sqlite()

def test_encrypt_and_decrypt(self):
self.log.info("Check that a silent payments wallet can be encrypted and decrypted")
self.log.info("Create encrypted wallet")
self.nodes[0].createwallet(wallet_name="sp_encrypted", passphrase="unsigned integer", silent_payment=True)
wallet = self.nodes[0].get_wallet_rpc("sp_encrypted")
addr = wallet.getnewaddress(address_type="silent-payment")
self.def_wallet.sendtoaddress(addr, 10)
self.generate(self.nodes[0], 1)
self.log.info("Check that we can scan without the wallet being unlocked")
assert_equal(wallet.getbalance(), 10)
self.log.info("Check that we get an error if trying to send with the wallet locked")
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wallet.sendtoaddress, addr, 9)
wallet.walletpassphrase(passphrase="unsigned integer", timeout=3)
self.log.info("Unlock wallet and send")
wallet.sendtoaddress(addr, 9)
self.generate(self.nodes[0], 1)
assert_approx(wallet.getbalance(), 10, 0.0001)

def test_encrypting_unencrypted(self):
self.log.info("Check that a silent payments wallet can be encrypted after creation")
self.log.info("Create un-encrypted wallet")
self.nodes[0].createwallet(wallet_name="sp_unencrypted", silent_payment=True)
wallet = self.nodes[0].get_wallet_rpc("sp_unencrypted")
addr = wallet.getnewaddress(address_type="silent-payment")
self.def_wallet.sendtoaddress(addr, 10)
self.generate(self.nodes[0], 1)
assert_equal(wallet.getbalance(), 10)
self.log.info("Add a passphrase to the wallet")
wallet.encryptwallet(passphrase="unsigned integer")
self.log.info("Check that we get an error if trying to send with the wallet locked")
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wallet.sendtoaddress, addr, 9)
wallet.walletpassphrase(passphrase="unsigned integer", timeout=3)
self.log.info("Unlock wallet and send")
wallet.sendtoaddress(addr, 9)
self.generate(self.nodes[0], 1)
assert_approx(wallet.getbalance(), 10, 0.0001)

def test_createwallet(self):
self.log.info("Check createwallet silent payments option")

Expand Down Expand Up @@ -69,6 +106,8 @@ def run_test(self):
self.generate(self.nodes[0], 101)

self.test_createwallet()
self.test_encrypt_and_decrypt()
self.test_encrypting_unencrypted()
self.test_basic()


Expand Down

0 comments on commit 29ae0a0

Please sign in to comment.