* Wallet fixes
* Fixing taproot testnet params * Bump version to 0.21.2rc3
This commit is contained in:
parent
4beb89b249
commit
149b4e5da5
@ -3,9 +3,9 @@ define(_CLIENT_VERSION_MAJOR, 0)
|
||||
define(_CLIENT_VERSION_MINOR, 21)
|
||||
define(_CLIENT_VERSION_REVISION, 2)
|
||||
define(_CLIENT_VERSION_BUILD, 0)
|
||||
define(_CLIENT_VERSION_RC, 2)
|
||||
define(_CLIENT_VERSION_RC, 3)
|
||||
define(_CLIENT_VERSION_IS_RELEASE, true)
|
||||
define(_COPYRIGHT_YEAR, 2021)
|
||||
define(_COPYRIGHT_YEAR, 2022)
|
||||
define(_COPYRIGHT_HOLDERS,[The %s developers])
|
||||
define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Litecoin Core]])
|
||||
AC_INIT([Litecoin Core],m4_join([.], _CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MINOR, _CLIENT_VERSION_REVISION, m4_if(_CLIENT_VERSION_BUILD, [0], [], _CLIENT_VERSION_BUILD))m4_if(_CLIENT_VERSION_RC, [0], [], [rc]_CLIENT_VERSION_RC),[https://github.com/litecoin-project/litecoin/issues],[litecoin],[https://litecoin.org/])
|
||||
|
||||
@ -207,8 +207,8 @@ public:
|
||||
|
||||
// Deployment of Taproot (BIPs 340-342)
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartHeight = 2225664; // March 2022
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeoutHeight = 2435328; // 364 days later
|
||||
|
||||
// Deployment of MWEB (LIP-0002, LIP-0003, and LIP-0004)
|
||||
consensus.vDeployments[Consensus::DEPLOYMENT_MWEB].bit = 4;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <mw/common/Macros.h>
|
||||
#include <mw/models/crypto/BlindingFactor.h>
|
||||
#include <mw/models/crypto/Commitment.h>
|
||||
#include <mw/models/wallet/StealthAddress.h>
|
||||
|
||||
#include <amount.h>
|
||||
#include <boost/optional.hpp>
|
||||
@ -10,30 +11,35 @@
|
||||
MW_NAMESPACE
|
||||
|
||||
/// <summary>
|
||||
/// Change outputs will use the stealth address generated using index 2,000,000.
|
||||
/// Change outputs will use the stealth address generated using index 0
|
||||
/// </summary>
|
||||
static constexpr uint32_t CHANGE_INDEX{0};
|
||||
|
||||
/// <summary>
|
||||
/// Peg-in outputs will use the stealth address generated using index 2,000,000.
|
||||
/// Peg-in outputs will use the stealth address generated using index 1.
|
||||
/// </summary>
|
||||
static constexpr uint32_t PEGIN_INDEX{1};
|
||||
|
||||
/// <summary>
|
||||
/// Outputs sent to others will be marked with an address_index of 0xffffffff.
|
||||
/// </summary>
|
||||
static constexpr uint32_t UNKNOWN_INDEX{std::numeric_limits<uint32_t>::max()};
|
||||
|
||||
/// <summary>
|
||||
/// Represents an output owned by the wallet, and the keys necessary to spend it.
|
||||
/// </summary>
|
||||
struct Coin : public Traits::ISerializable {
|
||||
// Version byte to more easily support adding new fields to the object.
|
||||
uint8_t version{0};
|
||||
uint8_t version{1};
|
||||
|
||||
// Index of the subaddress this coin was received at.
|
||||
uint32_t address_index;
|
||||
uint32_t address_index{UNKNOWN_INDEX};
|
||||
|
||||
// The private key needed in order to spend the coin.
|
||||
// May be empty for watch-only wallets.
|
||||
boost::optional<SecretKey> key;
|
||||
|
||||
// The blinding factor needed in order to spend the coin.
|
||||
// The blinding factor of the coin's output.
|
||||
// May be empty for watch-only wallets.
|
||||
boost::optional<BlindingFactor> blind;
|
||||
|
||||
@ -44,8 +50,18 @@ struct Coin : public Traits::ISerializable {
|
||||
// The output's ID (hash).
|
||||
mw::Hash output_id;
|
||||
|
||||
// The ephemeral private key used by the sender to create the output.
|
||||
// This will only be populated when the coin has flag HAS_SENDER_INFO.
|
||||
boost::optional<SecretKey> sender_key;
|
||||
|
||||
// The StealthAddress the coin was sent to.
|
||||
// This will only be populated when the coin has flag HAS_SENDER_INFO.
|
||||
boost::optional<StealthAddress> address;
|
||||
|
||||
bool IsChange() const noexcept { return address_index == CHANGE_INDEX; }
|
||||
bool IsPegIn() const noexcept { return address_index == PEGIN_INDEX; }
|
||||
bool IsMine() const noexcept { return address_index != UNKNOWN_INDEX; }
|
||||
bool HasAddress() const noexcept { return !!address; }
|
||||
|
||||
IMPL_SERIALIZABLE(Coin, obj)
|
||||
{
|
||||
@ -55,6 +71,11 @@ struct Coin : public Traits::ISerializable {
|
||||
READWRITE(obj.blind);
|
||||
READWRITE(VARINT_MODE(obj.amount, VarIntMode::NONNEGATIVE_SIGNED));
|
||||
READWRITE(obj.output_id);
|
||||
|
||||
if (obj.version >= 1) {
|
||||
READWRITE(obj.sender_key);
|
||||
READWRITE(obj.address);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ class TxBuilder
|
||||
BlindingFactor total_blind;
|
||||
SecretKey total_key;
|
||||
std::vector<Output> outputs;
|
||||
std::vector<mw::Coin> coins;
|
||||
};
|
||||
|
||||
public:
|
||||
@ -29,7 +30,8 @@ public:
|
||||
const std::vector<mw::Recipient>& recipients,
|
||||
const std::vector<PegOutCoin>& pegouts,
|
||||
const boost::optional<CAmount>& pegin_amount,
|
||||
const CAmount fee
|
||||
const CAmount fee,
|
||||
std::vector<mw::Coin>& output_coins
|
||||
);
|
||||
|
||||
private:
|
||||
|
||||
@ -75,7 +75,7 @@ Output Output::Create(
|
||||
Signature signature = Schnorr::Sign(sender_privkey.data(), sig_message);
|
||||
|
||||
if (blind_out != nullptr) {
|
||||
*blind_out = blind;
|
||||
*blind_out = mask.GetRawBlind();
|
||||
}
|
||||
|
||||
return Output{
|
||||
|
||||
@ -60,6 +60,7 @@ bool Keychain::RewindOutput(const Output& output, mw::Coin& coin) const
|
||||
coin.blind = boost::make_optional(mask.GetRawBlind());
|
||||
coin.amount = value;
|
||||
coin.output_id = output.GetOutputID();
|
||||
coin.address = wallet_addr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -10,7 +10,8 @@ mw::Transaction::CPtr TxBuilder::BuildTx(
|
||||
const std::vector<mw::Recipient>& recipients,
|
||||
const std::vector<PegOutCoin>& pegouts,
|
||||
const boost::optional<CAmount>& pegin_amount,
|
||||
const CAmount fee)
|
||||
const CAmount fee,
|
||||
std::vector<mw::Coin>& output_coins)
|
||||
{
|
||||
CAmount pegout_total = std::accumulate(
|
||||
pegouts.cbegin(), pegouts.cend(), (CAmount)0,
|
||||
@ -40,6 +41,7 @@ mw::Transaction::CPtr TxBuilder::BuildTx(
|
||||
|
||||
// Create outputs
|
||||
TxBuilder::Outputs outputs = CreateOutputs(recipients);
|
||||
output_coins = outputs.coins;
|
||||
|
||||
// Total kernel offset is split between raw kernel_offset and the kernel's blinding factor.
|
||||
// sum(output.blind) - sum(input.blind) = kernel_offset + sum(kernel.blind)
|
||||
@ -119,28 +121,36 @@ TxBuilder::Outputs TxBuilder::CreateOutputs(const std::vector<mw::Recipient>& re
|
||||
Blinds output_blinds;
|
||||
Blinds output_keys;
|
||||
std::vector<Output> outputs;
|
||||
std::transform(
|
||||
recipients.cbegin(), recipients.cend(), std::back_inserter(outputs),
|
||||
[&output_blinds, &output_keys](const mw::Recipient& recipient) {
|
||||
BlindingFactor blind;
|
||||
SecretKey ephemeral_key = SecretKey::Random();
|
||||
Output output = Output::Create(
|
||||
&blind,
|
||||
ephemeral_key,
|
||||
recipient.address,
|
||||
recipient.amount
|
||||
);
|
||||
std::vector<mw::Coin> coins;
|
||||
|
||||
output_blinds.Add(blind);
|
||||
output_keys.Add(ephemeral_key);
|
||||
return output;
|
||||
}
|
||||
);
|
||||
for (const mw::Recipient& recipient : recipients) {
|
||||
BlindingFactor raw_blind;
|
||||
SecretKey ephemeral_key = SecretKey::Random();
|
||||
Output output = Output::Create(
|
||||
&raw_blind,
|
||||
ephemeral_key,
|
||||
recipient.address,
|
||||
recipient.amount
|
||||
);
|
||||
|
||||
output_blinds.Add(Pedersen::BlindSwitch(raw_blind, recipient.amount));
|
||||
output_keys.Add(ephemeral_key);
|
||||
outputs.push_back(output);
|
||||
|
||||
mw::Coin coin;
|
||||
coin.blind = raw_blind;
|
||||
coin.amount = recipient.amount;
|
||||
coin.output_id = output.GetOutputID();
|
||||
coin.sender_key = ephemeral_key;
|
||||
coin.address = recipient.address;
|
||||
coins.push_back(coin);
|
||||
}
|
||||
|
||||
return TxBuilder::Outputs{
|
||||
output_blinds.Total(),
|
||||
SecretKey(output_keys.Total().data()),
|
||||
std::move(outputs)
|
||||
std::move(outputs),
|
||||
std::move(coins)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <mw/models/tx/Output.h>
|
||||
#include <mw/models/wallet/StealthAddress.h>
|
||||
#include <mw/crypto/Bulletproofs.h>
|
||||
#include <mw/crypto/Pedersen.h>
|
||||
|
||||
TEST_NAMESPACE
|
||||
|
||||
@ -18,10 +19,11 @@ public:
|
||||
const StealthAddress& receiver_addr,
|
||||
const uint64_t amount)
|
||||
{
|
||||
BlindingFactor blinding_factor;
|
||||
Output output = Output::Create(&blinding_factor, sender_privkey, receiver_addr, amount);
|
||||
BlindingFactor raw_blind;
|
||||
Output output = Output::Create(&raw_blind, sender_privkey, receiver_addr, amount);
|
||||
BlindingFactor blind_switch = Pedersen::BlindSwitch(raw_blind, amount);
|
||||
|
||||
return TxOutput{ std::move(blinding_factor), amount, std::move(output) };
|
||||
return TxOutput{std::move(blind_switch), amount, std::move(output)};
|
||||
}
|
||||
|
||||
const BlindingFactor& GetBlind() const noexcept { return m_blindingFactor; }
|
||||
|
||||
@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(Create)
|
||||
receiver_subaddr,
|
||||
amount
|
||||
);
|
||||
Commitment expected_commit = Commitment::Blinded(blind, amount);
|
||||
Commitment expected_commit = Commitment::Switch(blind, amount);
|
||||
|
||||
// Verify bulletproof
|
||||
ProofData proof_data = output.BuildProofData();
|
||||
|
||||
@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(TxUTXO)
|
||||
StealthAddress::Random(),
|
||||
amount
|
||||
);
|
||||
Commitment commit = Commitment::Blinded(blind, amount);
|
||||
Commitment commit = Commitment::Switch(blind, amount);
|
||||
|
||||
int32_t blockHeight = 20;
|
||||
mmr::LeafIndex leafIndex = mmr::LeafIndex::At(5);
|
||||
|
||||
@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(Create)
|
||||
receiver_subaddr,
|
||||
amount
|
||||
);
|
||||
Commitment expected_commit = Commitment::Blinded(blind, amount);
|
||||
Commitment expected_commit = Commitment::Switch(blind, amount);
|
||||
|
||||
// Verify bulletproof
|
||||
ProofData proof_data = output.BuildProofData();
|
||||
|
||||
@ -88,18 +88,29 @@ bool Transact::CreateTx(
|
||||
if (change_amount < 0) {
|
||||
return false;
|
||||
}
|
||||
StealthAddress change_address = mweb_wallet->GetStealthAddress(mw::CHANGE_INDEX);
|
||||
StealthAddress change_address;
|
||||
if (!mweb_wallet->GetStealthAddress(mw::CHANGE_INDEX, change_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
receivers.push_back(mw::Recipient{change_amount, change_address});
|
||||
}
|
||||
|
||||
// Create transaction
|
||||
std::vector<mw::Coin> input_coins = GetInputCoins(selected_coins);
|
||||
std::vector<mw::Coin> output_coins;
|
||||
transaction.mweb_tx = TxBuilder::BuildTx(
|
||||
input_coins,
|
||||
receivers,
|
||||
pegouts,
|
||||
pegin_amount,
|
||||
mweb_fee);
|
||||
mweb_fee,
|
||||
output_coins
|
||||
);
|
||||
|
||||
if (!output_coins.empty()) {
|
||||
mweb_wallet->SaveToWallet(output_coins);
|
||||
}
|
||||
|
||||
// Update pegin output
|
||||
auto pegins = transaction.mweb_tx.GetPegIns();
|
||||
|
||||
@ -23,7 +23,7 @@ std::vector<mw::Coin> Wallet::RewindOutputs(const CTransaction& tx)
|
||||
bool Wallet::RewindOutput(const boost::variant<mw::Block::CPtr, mw::Transaction::CPtr>& parent,
|
||||
const mw::Hash& output_id, mw::Coin& coin)
|
||||
{
|
||||
if (GetCoin(output_id, coin)) {
|
||||
if (GetCoin(output_id, coin) && coin.IsMine()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -61,14 +61,29 @@ bool Wallet::RewindOutput(const boost::variant<mw::Block::CPtr, mw::Transaction:
|
||||
|
||||
bool Wallet::IsChange(const StealthAddress& address) const
|
||||
{
|
||||
return IsSupported() && address == GetStealthAddress(mw::CHANGE_INDEX);
|
||||
StealthAddress change_addr;
|
||||
return GetStealthAddress(mw::CHANGE_INDEX, change_addr) && change_addr == address;
|
||||
}
|
||||
|
||||
StealthAddress Wallet::GetStealthAddress(const uint32_t index) const
|
||||
bool Wallet::GetStealthAddress(const mw::Coin& coin, StealthAddress& address) const
|
||||
{
|
||||
if (coin.HasAddress()) {
|
||||
address = *coin.address;
|
||||
return true;
|
||||
}
|
||||
|
||||
return GetStealthAddress(coin.address_index, address);
|
||||
}
|
||||
|
||||
bool Wallet::GetStealthAddress(const uint32_t index, StealthAddress& address) const
|
||||
{
|
||||
mw::Keychain::Ptr keychain = GetKeychain();
|
||||
assert(keychain != nullptr);
|
||||
return keychain->GetStealthAddress(index);
|
||||
if (!keychain || index == mw::UNKNOWN_INDEX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
address = keychain->GetStealthAddress(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Wallet::LoadToWallet(const mw::Coin& coin)
|
||||
@ -76,6 +91,15 @@ void Wallet::LoadToWallet(const mw::Coin& coin)
|
||||
m_coins[coin.output_id] = coin;
|
||||
}
|
||||
|
||||
void Wallet::SaveToWallet(const std::vector<mw::Coin>& coins)
|
||||
{
|
||||
WalletBatch batch(m_pWallet->GetDatabase());
|
||||
for (const mw::Coin& coin : coins) {
|
||||
m_coins[coin.output_id] = coin;
|
||||
batch.WriteMWEBCoin(coin);
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::GetCoin(const mw::Hash& output_id, mw::Coin& coin) const
|
||||
{
|
||||
auto iter = m_coins.find(output_id);
|
||||
|
||||
@ -37,9 +37,11 @@ public:
|
||||
const mw::Hash& output_id,
|
||||
mw::Coin& coin
|
||||
);
|
||||
StealthAddress GetStealthAddress(const uint32_t index) const;
|
||||
bool GetStealthAddress(const mw::Coin& coin, StealthAddress& address) const;
|
||||
bool GetStealthAddress(const uint32_t index, StealthAddress& address) const;
|
||||
|
||||
void LoadToWallet(const mw::Coin& coin);
|
||||
void SaveToWallet(const std::vector<mw::Coin>& coins);
|
||||
|
||||
private:
|
||||
mw::Keychain::Ptr GetKeychain() const;
|
||||
|
||||
@ -184,11 +184,15 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
|
||||
tr("Could not unlock wallet."),
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
break;
|
||||
case AddressTableModel::EditStatus::KEY_GENERATION_FAILURE:
|
||||
QMessageBox::critical(this, windowTitle(),
|
||||
tr("Could not generate new %1 address").arg(QString::fromStdString(FormatOutputType(address_type))),
|
||||
QMessageBox::Ok, QMessageBox::Ok);
|
||||
case AddressTableModel::EditStatus::KEY_GENERATION_FAILURE: {
|
||||
QString message = tr("Could not generate new %1 address.").arg(QString::fromStdString(FormatOutputType(address_type)));
|
||||
if (address_type == OutputType::MWEB) {
|
||||
message += tr("\nTry upgrading your wallet.");
|
||||
}
|
||||
|
||||
QMessageBox::critical(this, windowTitle(), message, QMessageBox::Ok, QMessageBox::Ok);
|
||||
break;
|
||||
}
|
||||
// These aren't valid return values for our action
|
||||
case AddressTableModel::EditStatus::INVALID_ADDRESS:
|
||||
case AddressTableModel::EditStatus::DUPLICATE_ADDRESS:
|
||||
|
||||
@ -275,7 +275,12 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa
|
||||
// Reserve pegout keys and set pegout addresses
|
||||
for (SendCoinsRecipient& rcp : recipients) {
|
||||
if (rcp.type == SendCoinsRecipient::MWEB_PEGIN) {
|
||||
rcp.address = QString::fromStdString(EncodeDestination(model->wallet().getPeginAddress()));
|
||||
StealthAddress pegin_address;
|
||||
if (!model->wallet().getPeginAddress(pegin_address)) {
|
||||
fNewRecipientAllowed = true;
|
||||
return false;
|
||||
}
|
||||
rcp.address = QString::fromStdString(EncodeDestination(pegin_address));
|
||||
} else if (rcp.type == SendCoinsRecipient::MWEB_PEGOUT) {
|
||||
CTxDestination dest;
|
||||
auto reserved_dest = model->wallet().reserveNewDestination(dest);
|
||||
@ -1015,8 +1020,10 @@ void SendCoinsDialog::mwebPegInButtonClicked(bool checked)
|
||||
ui->pushButtonMWEBPegOut->setChecked(false);
|
||||
|
||||
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
|
||||
if (checked) {
|
||||
entry->setPegInAddress(EncodeDestination(model->wallet().getPeginAddress()));
|
||||
|
||||
StealthAddress pegin_address;
|
||||
if (checked && model->wallet().getPeginAddress(pegin_address)) {
|
||||
entry->setPegInAddress(EncodeDestination(pegin_address));
|
||||
} else {
|
||||
entry->setPegInAddress("");
|
||||
}
|
||||
|
||||
@ -180,7 +180,12 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
|
||||
switch (rcp.type) {
|
||||
case SendCoinsRecipient::MWEB_PEGIN: {
|
||||
coin_control_copy.fPegIn = true;
|
||||
receiver = m_wallet->getPeginAddress();
|
||||
StealthAddress pegin_address;
|
||||
if (!m_wallet->getPeginAddress(pegin_address)) {
|
||||
return TransactionCreationFailed;
|
||||
}
|
||||
|
||||
receiver = pegin_address;
|
||||
break;
|
||||
}
|
||||
case SendCoinsRecipient::MWEB_PEGOUT: {
|
||||
|
||||
@ -1099,10 +1099,10 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
|
||||
if (nDepth < nMinDepth)
|
||||
continue;
|
||||
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
for (const CTxOutput& txout : wtx.tx->GetOutputs())
|
||||
{
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(txout.scriptPubKey, address))
|
||||
if (!pwallet->ExtractOutputDestination(txout, address))
|
||||
continue;
|
||||
|
||||
if (has_filtered_address && !(filtered_address == address)) {
|
||||
@ -1114,7 +1114,7 @@ static UniValue ListReceived(const CWallet* const pwallet, const UniValue& param
|
||||
continue;
|
||||
|
||||
tallyitem& item = mapTally[address];
|
||||
item.nAmount += txout.nValue;
|
||||
item.nAmount += pwallet->GetValue(txout);
|
||||
item.nConf = std::min(item.nConf, nDepth);
|
||||
item.txids.push_back(wtx.GetHash());
|
||||
if (mine & ISMINE_WATCH_ONLY)
|
||||
|
||||
@ -1351,7 +1351,7 @@ isminetype CWallet::IsMine(const CTxInput& input) const
|
||||
AssertLockHeld(cs_wallet);
|
||||
if (input.IsMWEB()) {
|
||||
mw::Coin coin;
|
||||
return GetCoin(input.ToMWEB(), coin) ? ISMINE_SPENDABLE : ISMINE_NO;
|
||||
return GetCoin(input.ToMWEB(), coin) && coin.IsMine() ? ISMINE_SPENDABLE : ISMINE_NO;
|
||||
}
|
||||
|
||||
const CWalletTx* prev = FindPrevTx(input);
|
||||
@ -1372,7 +1372,7 @@ CAmount CWallet::GetDebit(const CTxInput& input, const isminefilter& filter) con
|
||||
LOCK(cs_wallet);
|
||||
if (input.IsMWEB()) {
|
||||
mw::Coin coin;
|
||||
if ((filter & ISMINE_SPENDABLE) && GetCoin(input.ToMWEB(), coin)) {
|
||||
if ((filter & ISMINE_SPENDABLE) && GetCoin(input.ToMWEB(), coin) && coin.IsMine()) {
|
||||
return coin.amount;
|
||||
}
|
||||
|
||||
@ -1397,7 +1397,7 @@ isminetype CWallet::IsMine(const CTxOutput& output) const
|
||||
AssertLockHeld(cs_wallet);
|
||||
if (output.IsMWEB()) {
|
||||
mw::Coin coin;
|
||||
return GetCoin(output.ToMWEB(), coin) ? ISMINE_SPENDABLE : ISMINE_NO;
|
||||
return GetCoin(output.ToMWEB(), coin) && coin.IsMine() ? ISMINE_SPENDABLE : ISMINE_NO;
|
||||
}
|
||||
|
||||
return IsMine(DestinationAddr(output.GetScriptPubKey()));
|
||||
@ -1433,7 +1433,7 @@ bool CWallet::IsChange(const CTxOutput& output) const
|
||||
if (output.IsMWEB()) {
|
||||
mw::Coin coin;
|
||||
if (GetCoin(output.ToMWEB(), coin)) {
|
||||
return coin.address_index == mw::CHANGE_INDEX;
|
||||
return coin.IsChange();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1494,7 +1494,7 @@ bool CWallet::IsFromMe(const CTransaction& tx) const
|
||||
CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const
|
||||
{
|
||||
CAmount nDebit = 0;
|
||||
for (const CTxIn& txin : tx.vin)
|
||||
for (const CTxInput& txin : tx.GetInputs())
|
||||
{
|
||||
nDebit += GetDebit(txin, filter);
|
||||
if (!MoneyRange(nDebit))
|
||||
@ -2061,7 +2061,7 @@ CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter
|
||||
{
|
||||
auto& amount = m_amounts[type];
|
||||
if (recalculate || !amount.m_cached[filter]) {
|
||||
amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter));
|
||||
amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter)); // MW: TODO - Need to support partial mweb info
|
||||
m_is_cache_empty = false;
|
||||
}
|
||||
return amount.m_value[filter];
|
||||
@ -2456,7 +2456,11 @@ void CWallet::AvailableCoins(std::vector<COutputCoin>& vCoins, bool fOnlySafe, c
|
||||
continue;
|
||||
}
|
||||
|
||||
StealthAddress address = mweb_wallet->GetStealthAddress(coin.address_index);
|
||||
StealthAddress address;
|
||||
if (!mweb_wallet->GetStealthAddress(coin, address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vCoins.push_back(MWOutput{coin, nDepth, address, &wtx});
|
||||
} else {
|
||||
size_t i = boost::get<COutPoint>(output.GetIndex()).n;
|
||||
@ -2511,7 +2515,7 @@ std::map<CTxDestination, std::vector<COutputCoin>> CWallet::ListCoins() const
|
||||
if (ExtractOutputDestination(FindNonChangeParentOutput(*wtx->tx, output_idx), address)) {
|
||||
if (output_idx.type() == typeid(mw::Hash)) {
|
||||
mw::Coin coin;
|
||||
if (GetCoin(boost::get<mw::Hash>(output_idx), coin)) {
|
||||
if (GetCoin(boost::get<mw::Hash>(output_idx), coin) && coin.IsMine()) {
|
||||
result[address].emplace_back(MWOutput{coin, depth, boost::get<StealthAddress>(address), wtx});
|
||||
}
|
||||
} else {
|
||||
@ -2618,7 +2622,7 @@ bool CWallet::SelectCoins(const std::vector<COutputCoin>& vAvailableCoins, const
|
||||
{
|
||||
if (idx.type() == typeid(mw::Hash)) {
|
||||
mw::Coin mweb_coin;
|
||||
if (!GetCoin(boost::get<mw::Hash>(idx), mweb_coin)) {
|
||||
if (!GetCoin(boost::get<mw::Hash>(idx), mweb_coin) || !mweb_coin.IsMine()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4331,7 +4335,12 @@ bool CWallet::ExtractOutputDestination(const CTxOutput& output, CTxDestination&
|
||||
return false;
|
||||
}
|
||||
|
||||
dest = mweb_wallet->GetStealthAddress(coin.address_index);
|
||||
StealthAddress address;
|
||||
if (!mweb_wallet->GetStealthAddress(coin, address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest = address;
|
||||
return true;
|
||||
} else {
|
||||
return ExtractDestination(output.GetScriptPubKey(), dest);
|
||||
@ -4346,7 +4355,13 @@ bool CWallet::ExtractDestinationScript(const CTxOutput& output, DestinationAddr&
|
||||
return false;
|
||||
}
|
||||
|
||||
dest = mweb_wallet->GetStealthAddress(coin.address_index);
|
||||
StealthAddress address;
|
||||
if (!mweb_wallet->GetStealthAddress(coin, address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest = address;
|
||||
return true;
|
||||
} else {
|
||||
dest = DestinationAddr(output.GetScriptPubKey());
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
class MWEBWalletPreHDTest(BitcoinTestFramework):
|
||||
class MWEBWalletAddressTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
@ -56,8 +56,13 @@ class MWEBWalletPreHDTest(BitcoinTestFramework):
|
||||
|
||||
self.start_nodes()
|
||||
self.import_deterministic_coinbase_privkeys()
|
||||
|
||||
|
||||
def run_test(self):
|
||||
self.test_prehd_wallet()
|
||||
# TODO: self.test_blank_wallet()
|
||||
# TODO: self.test_keys_disabled()
|
||||
|
||||
def test_prehd_wallet(self):
|
||||
self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
|
||||
|
||||
node_master = self.nodes[0]
|
||||
@ -82,4 +87,4 @@ class MWEBWalletPreHDTest(BitcoinTestFramework):
|
||||
assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", node_master.getnewaddress, address_type='mweb')
|
||||
|
||||
if __name__ == '__main__':
|
||||
MWEBWalletPreHDTest().main()
|
||||
MWEBWalletAddressTest().main()
|
||||
71
test/functional/mweb_wallet_basic.py
Normal file
71
test/functional/mweb_wallet_basic.py
Normal file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2021 The Litecoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Basic MWEB Wallet test"""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.ltc_util import setup_mweb_chain
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
class MWEBWalletBasicTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
self.extra_args = [['-whitelist=noban@127.0.0.1'],[]] # immediate tx relay
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def run_test(self):
|
||||
node0 = self.nodes[0]
|
||||
node1 = self.nodes[1]
|
||||
|
||||
self.log.info("Setting up MWEB chain")
|
||||
setup_mweb_chain(node0)
|
||||
self.sync_all()
|
||||
|
||||
self.log.info("Send to node1 mweb address")
|
||||
n1_addr0 = node1.getnewaddress(address_type='mweb')
|
||||
tx1_id = node0.sendtoaddress(n1_addr0, 25)
|
||||
self.sync_mempools()
|
||||
|
||||
self.log.info("Verify node0's wallet lists the transaction as spent")
|
||||
n0_tx1 = node0.gettransaction(txid=tx1_id)
|
||||
assert_equal(n0_tx1['confirmations'], 0)
|
||||
assert n0_tx1['amount'] == -25
|
||||
assert n0_tx1['fee'] < 0 and n0_tx1['fee'] > -0.1
|
||||
|
||||
self.log.info("Verify node1's wallet receives the unconfirmed transaction")
|
||||
n1_addr0_coins = node1.listreceivedbyaddress(minconf=0, address_filter=n1_addr0)
|
||||
assert_equal(len(n1_addr0_coins), 1)
|
||||
assert n1_addr0_coins[0]['amount'] == 25
|
||||
assert n1_addr0_coins[0]['confirmations'] == 0
|
||||
assert node1.getbalances()['mine']['untrusted_pending'] == 25
|
||||
assert node1.getbalances()['mine']['trusted'] == 0
|
||||
|
||||
self.log.info("Mine the next block")
|
||||
node0.generate(1)
|
||||
self.sync_blocks()
|
||||
|
||||
self.log.info("Verify node1's wallet lists the transaction as confirmed")
|
||||
n1_addr0_coins = node1.listunspent(addresses=[n1_addr0])
|
||||
assert_equal(len(n1_addr0_coins), 1)
|
||||
assert n1_addr0_coins[0]['amount'] == 25
|
||||
assert n1_addr0_coins[0]['confirmations'] == 1
|
||||
assert node1.getbalances()['mine']['untrusted_pending'] == 0
|
||||
assert node1.getbalances()['mine']['trusted'] == 25
|
||||
|
||||
self.log.info("Verify node0's wallet lists the transaction as confirmed")
|
||||
n0_tx1 = node0.gettransaction(txid=tx1_id)
|
||||
assert_equal(n0_tx1['confirmations'], 1)
|
||||
assert n0_tx1['amount'] == -25
|
||||
assert n0_tx1['fee'] < 0 and n0_tx1['fee'] > -0.1
|
||||
|
||||
|
||||
# TODO: Conflicting txs
|
||||
# TODO: Duplicate hash
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
MWEBWalletBasicTest().main()
|
||||
@ -250,7 +250,8 @@ BASE_SCRIPTS = [
|
||||
'mweb_reorg.py',
|
||||
'mweb_pegout_all.py',
|
||||
'mweb_node_compatibility.py',
|
||||
'mweb_wallet_prehd.py',
|
||||
'mweb_wallet_address.py',
|
||||
'mweb_wallet_basic.py',
|
||||
'rpc_uptime.py',
|
||||
'wallet_resendwallettransactions.py',
|
||||
'wallet_resendwallettransactions.py --descriptors',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user