Skip to content

Commit

Permalink
Merge #1156: [backport] Backport fixes from master to elements-22.x f…
Browse files Browse the repository at this point in the history
…or rc5

37076d7 Bump version to -rc5 (Pablo Greco)
5629cae fs: Make compatible with boost 1.78 (Andrew Chow)
60b913e Elements-qt: Correctly display the amount in the sender's wallet after using Send button (Andrea Bonel)
872478b docs: describe elements transaction serialization format (#1148) (James Dorfman)
dd2d758 fixed minor issues found in review (Allen Piscitello)
2da7d75 removing test that fails due to blinded issuances, which results in incorrect reissuance token ids (Allen Piscitello)
420de43 removing code to blind issuances. PSET should be modified to include an option to blind or unblind issuances, defaulting to unblind. (Allen Piscitello)

Pull request description:

  Backport #1150 #1154 and #1148 from master.
  Backport bitcoin/bitcoin#24104 from Bitcoin Core
  Bump to -rc5

ACKs for top commit:
  jamesdorfman:
    utACK 37076d7.
  delta1:
    utACK 37076d7

Tree-SHA512: 616322f94c17008cc7fd582203c22b33c73b922269ab33fd13b270f491eda9b30d4f18efa3f7887e247bef46b63c4810f4084e409bea98120702c426a83343a8
  • Loading branch information
apoelstra committed Sep 6, 2022
2 parents 580230f + 37076d7 commit 433a57a
Show file tree
Hide file tree
Showing 14 changed files with 671 additions and 109 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ AC_PREREQ([2.69])
define(_CLIENT_VERSION_MAJOR, 22)
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_RC, 4)
define(_CLIENT_VERSION_RC, 5)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2022)
define(_COPYRIGHT_HOLDERS,[The %s developers])
Expand Down
595 changes: 595 additions & 0 deletions doc/elements-tx-format.md

Large diffs are not rendered by default.

113 changes: 34 additions & 79 deletions src/blindpsbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,99 +386,54 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
}

// Handle issuances
if (input.m_issuance_value) {
if (!input.m_issuance_value_commitment.IsCommitment() && input.m_issuance_rangeproof.size() == 0 && input.m_issuance_inflation_keys_rangeproof.size() == 0) {
CAsset issuance_asset;
CAsset reissuance_asset;

uint256 entropy;
if (!input.m_issuance_blinding_nonce.IsNull()) {
// Reissuance, use assetEntropy as the asset entropy
entropy = input.m_issuance_asset_entropy;
} else {
// New issuance, make new entropy
GenerateAssetEntropy(entropy, input.GetOutPoint(), input.m_issuance_asset_entropy);
}
if (input.m_issuance_value != std::nullopt || input.m_issuance_value_commitment.IsCommitment() || input.m_issuance_inflation_keys_amount != std::nullopt || input.m_issuance_inflation_keys_commitment.IsCommitment()) {
CAsset issuance_asset;
CAsset reissuance_asset;

uint256 entropy;
if (!input.m_issuance_blinding_nonce.IsNull()) {
// Reissuance, use assetEntropy as the asset entropy
entropy = input.m_issuance_asset_entropy;
} else {
// New issuance, make new entropy
GenerateAssetEntropy(entropy, input.GetOutPoint(), input.m_issuance_asset_entropy);
}

if (input.m_issuance_value != std::nullopt || input.m_issuance_value_commitment.IsCommitment()) {
// Asset isn't blinded yet. Add it to the list of input assets
CalculateAsset(issuance_asset, entropy);
fixed_input_tags.emplace_back();
memcpy(fixed_input_tags.back().data, issuance_asset.begin(), 32);
ephemeral_input_tags.emplace_back();
if (secp256k1_generator_generate(secp256k1_blind_context, &ephemeral_input_tags.back(), issuance_asset.begin()) != 1) {
return BlindingStatus::INVALID_ASSET;
if (input.m_issuance_value_commitment.IsNull()) {
if (secp256k1_generator_generate(secp256k1_blind_context, &ephemeral_input_tags.back(), issuance_asset.begin()) != 1) {
return BlindingStatus::INVALID_ASSET;
}
}
unsigned int iss_to_blind = 1; // Always do the first issuance blinding iteration for the issuance value
else {
memcpy(ephemeral_input_tags.back().data, input.m_issuance_value_commitment.vchCommitment.data(), 33);
}
input_asset_blinders.emplace_back();
}

bool blind_issuance = our_issuances_to_blind.count(i) > 0;
bool blind_issuance = input.m_issuance_value_commitment.IsCommitment();

if (input.m_issuance_blinding_nonce.IsNull() && input.m_issuance_inflation_keys_amount) {
// New issuance, do reissuance token things
CalculateReissuanceToken(reissuance_asset, entropy, blind_issuance);
// Add the reissuance_asset to the list of input assets
fixed_input_tags.emplace_back();
memcpy(fixed_input_tags.back().data, reissuance_asset.begin(), 32);
ephemeral_input_tags.emplace_back();
if (input.m_issuance_blinding_nonce.IsNull() && (input.m_issuance_inflation_keys_amount != std::nullopt || input.m_issuance_inflation_keys_commitment.IsCommitment())) {
// New issuance, do reissuance token things
CalculateReissuanceToken(reissuance_asset, entropy, blind_issuance);
// Add the reissuance_asset to the list of input assets
fixed_input_tags.emplace_back();
memcpy(fixed_input_tags.back().data, reissuance_asset.begin(), 32);
ephemeral_input_tags.emplace_back();
if (input.m_issuance_inflation_keys_commitment.IsNull()) {
if (secp256k1_generator_generate(secp256k1_blind_context, &ephemeral_input_tags.back(), reissuance_asset.begin()) != 1) {
return BlindingStatus::INVALID_ASSET;
}
iss_to_blind++; // If we have a reissuance, do the second blinding iteration for the inflation keys
}

if (blind_issuance) {
for (unsigned int blind_i = 0; blind_i < iss_to_blind; ++blind_i) {
// To blind an issuance, both the issuance value and the number of inflation keys need to be blinded
// Since this process is basically the same for both, do it in a loop and switch based on the index
bool blind_value = blind_i == 0; // True for blinding the value, false for blinding the inflation keys
CAmount value = blind_value ? *input.m_issuance_value : *input.m_issuance_inflation_keys_amount;
CAsset asset = blind_value ? issuance_asset : reissuance_asset;
CKey blinding_privkey = blind_value ? our_issuances_to_blind.at(i).first : our_issuances_to_blind.at(i).second;

uint256 value_blinder;
GetStrongRandBytes(value_blinder.begin(), value_blinder.size());

// Create unblinded generator. Throw away everything except asset_gen
uint256 asset_blinder;
CConfidentialAsset conf_asset;
secp256k1_generator asset_gen;
CreateAssetCommitment(conf_asset, asset_gen, asset, asset_blinder);
input_asset_blinders.push_back(asset_blinder);

// Compute the scalar for this blinding and add to the input scalar
if (!ComputeAndAddToScalarOffset(input_scalar, value, asset_blinder, value_blinder)) return BlindingStatus::SCALAR_UNABLE;

// Create value commitment
secp256k1_pedersen_commitment value_commit;
CConfidentialValue conf_value;
CreateValueCommitment(conf_value, value_commit, value_blinder, asset_gen, value);

// Nonce is the blinding key
uint256 nonce = uint256(std::vector<unsigned char>(blinding_privkey.begin(), blinding_privkey.end()));

// Generate rangeproof
std::vector<unsigned char> rangeproof;
bool rangeresult = CreateValueRangeProof(rangeproof, value_blinder, nonce, value, CScript(), value_commit, asset_gen, asset, asset_blinder);
assert(rangeresult);

// Create explicit value rangeproofs
std::vector<unsigned char> blind_value_proof;
rangeresult = CreateBlindValueProof(blind_value_proof, value_blinder, value, value_commit, asset_gen);
assert(rangeresult);

if (blind_value) {
input.m_issuance_value_commitment = conf_value;
input.m_issuance_rangeproof = rangeproof;
input.m_blind_issuance_value_proof = blind_value_proof;
} else {
input.m_issuance_inflation_keys_commitment = conf_value;
input.m_issuance_inflation_keys_rangeproof = rangeproof;
input.m_blind_issuance_inflation_keys_proof = blind_value_proof;
}
}
}
else {
input_asset_blinders.emplace_back();
else if(input.m_issuance_inflation_keys_commitment.IsCommitment()){
memcpy(ephemeral_input_tags.back().data, input.m_issuance_inflation_keys_commitment.vchCommitment.data(), 33);
}
input_asset_blinders.emplace_back();
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/interfaces/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct PartiallySignedTransaction;
struct WalletContext;
struct bilingual_str;
typedef uint8_t isminefilter;
struct BlindDetails;

namespace interfaces {

Expand Down Expand Up @@ -140,13 +141,14 @@ class Wallet
bool sign,
int& change_pos,
CAmount& fee,
std::vector<CAmount>& out_amounts,
BlindDetails* blind_details,
bilingual_str& fail_reason) = 0;

//! Commit transaction.
virtual void commitTransaction(CTransactionRef tx,
WalletValueMap value_map,
WalletOrderForm order_form) = 0;
WalletOrderForm order_form,
BlindDetails* blind_details) = 0;

//! Return whether transaction can be abandoned.
virtual bool transactionCanBeAbandoned(const uint256& txid) = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ CMutableTransaction PartiallySignedTransaction::GetUnsignedTx(bool force_unblind
txin.assetIssuance.nAmount.SetNull();
}
if (input.m_issuance_inflation_keys_amount != std::nullopt && (input.m_issuance_inflation_keys_commitment.IsNull() || force_unblinded)) {
txin.assetIssuance.nInflationKeys.SetToAmount(*input.m_issuance_value);
txin.assetIssuance.nInflationKeys.SetToAmount(*input.m_issuance_inflation_keys_amount);
}
else if(!input.m_issuance_inflation_keys_commitment.IsNull()) {
txin.assetIssuance.nInflationKeys = input.m_issuance_inflation_keys_commitment;
Expand Down
11 changes: 8 additions & 3 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,13 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa

// prepare transaction for getting txFee earlier
m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
if (g_con_elementsmode)
m_current_blind_details = std::make_unique<BlindDetails>();
WalletModel::SendCoinsReturn prepareStatus;

updateCoinControlState();

prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control);
prepareStatus = model->prepareTransaction(*m_current_transaction, m_current_blind_details.get(), *m_coin_control);

// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
Expand Down Expand Up @@ -403,6 +405,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
QString question_string, informative_text, detailed_text;
if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
assert(m_current_transaction);
assert(!g_con_elementsmode || m_current_blind_details);

const QString confirmation = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
const QString confirmButtonText = model->wallet().privateKeysDisabled() && !model->wallet().hasExternalSigner() ? tr("Create Unsigned") : tr("Sign and send");
Expand Down Expand Up @@ -461,7 +464,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (complete) {
const CTransactionRef tx = MakeTransactionRef(mtx);
m_current_transaction->setWtx(tx);
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction, m_current_blind_details.get());
// process sendStatus and on error generate message shown to user
processSendCoinsReturn(sendStatus);

Expand Down Expand Up @@ -520,7 +523,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
} // msgBox.exec()
} else {
// now send the prepared transaction
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction);
WalletModel::SendCoinsReturn sendStatus = model->sendCoins(*m_current_transaction, m_current_blind_details.get());
// process sendStatus and on error generate message shown to user
processSendCoinsReturn(sendStatus);

Expand All @@ -537,11 +540,13 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
}
fNewRecipientAllowed = true;
m_current_transaction.reset();
m_current_blind_details.reset();
}

void SendCoinsDialog::clear()
{
m_current_transaction.reset();
m_current_blind_details.reset();

// Clear coin control settings
m_coin_control->UnSelectAll();
Expand Down
1 change: 1 addition & 0 deletions src/qt/sendcoinsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public Q_SLOTS:
WalletModel *model;
std::unique_ptr<CCoinControl> m_coin_control;
std::unique_ptr<WalletModelTransaction> m_current_transaction;
std::unique_ptr<BlindDetails> m_current_blind_details;
bool fNewRecipientAllowed;
bool fFeeMinimized;
const PlatformStyle *platformStyle;
Expand Down
13 changes: 8 additions & 5 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ bool WalletModel::validateAddress(const QString &address)
return IsValidDestinationString(address.toStdString());
}

WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, BlindDetails *blind_details, const CCoinControl& coinControl)
{
CAmountMap total;
bool fSubtractFeeFromAmount = false;
Expand Down Expand Up @@ -236,10 +236,13 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact

auto& newTx = transaction.getWtx();
std::vector<CAmount> out_amounts;
newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, out_amounts, error);
newTx = m_wallet->createTransaction(vecSend, coinControl, !wallet().privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, blind_details, error);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx) {
assert(out_amounts.size() == newTx->vout.size());
if(blind_details) {
out_amounts = blind_details->o_amounts;
assert(out_amounts.size() == newTx->vout.size());
}
transaction.reassignAmounts(out_amounts, nChangePosRet);
}

Expand All @@ -266,7 +269,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return SendCoinsReturn(OK);
}

WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction, BlindDetails *blind_details)
{
QByteArray transaction_array; /* store serialized transaction */

Expand All @@ -279,7 +282,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
}

auto& newTx = transaction.getWtx();
wallet().commitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm));
wallet().commitTransaction(newTx, {} /* mapValue */, std::move(vOrderForm), blind_details);

CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *newTx;
Expand Down
4 changes: 2 additions & 2 deletions src/qt/walletmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ class WalletModel : public QObject
};

// prepare transaction for getting txfee before sending coins
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl);
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, BlindDetails *blind_details, const CCoinControl& coinControl);

// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
SendCoinsReturn sendCoins(WalletModelTransaction &transaction, BlindDetails *blind_details);

// Wallet encryption
bool setWalletEncrypted(const SecureString& passphrase);
Expand Down
11 changes: 5 additions & 6 deletions src/wallet/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,26 +248,25 @@ class WalletImpl : public Wallet
bool sign,
int& change_pos,
CAmount& fee,
std::vector<CAmount>& out_amounts,
BlindDetails* blind_details,
bilingual_str& fail_reason) override
{
LOCK(m_wallet->cs_wallet);
CTransactionRef tx;
FeeCalculation fee_calc_out;
BlindDetails blind_details;
if (!m_wallet->CreateTransaction(recipients, tx, fee, change_pos,
fail_reason, coin_control, fee_calc_out, sign, gArgs.GetBoolArg("-blindedaddresses", g_con_elementsmode) ? &blind_details : nullptr)) {
fail_reason, coin_control, fee_calc_out, sign, blind_details)) {
return {};
}
out_amounts = blind_details.o_amounts;
return tx;
}
void commitTransaction(CTransactionRef tx,
WalletValueMap value_map,
WalletOrderForm order_form) override
WalletOrderForm order_form,
BlindDetails* blind_details) override
{
LOCK(m_wallet->cs_wallet);
m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form));
m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form), blind_details);
}
bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet->TransactionCanBeAbandoned(txid); }
bool abandonTransaction(const uint256& txid) override
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/load.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ bool VerifyWallets(interfaces::Chain& chain)
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
boost::system::error_code error;
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error).remove_trailing_separator();
if (error || !fs::exists(wallet_dir)) {
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1921,8 +1921,8 @@ BlindingStatus CWallet::WalletBlindPSBT(PartiallySignedTransaction& psbtx) const
our_input_data[i] = std::make_tuple(amount, asset, asset_blinder, value_blinder);
}

// Blind issuances on our inputs
if (input.m_issuance_value || input.m_issuance_inflation_keys_amount) {
// Blind issuances on our inputs if at least one commitment was provided.
if (input.m_issuance_value_commitment.IsCommitment() || input.m_issuance_inflation_keys_commitment.IsCommitment()) {
CScript blinding_script(CScript() << OP_RETURN << std::vector<unsigned char>(input.prev_txid.begin(), input.prev_txid.end()) << *input.prev_out);
our_issuances_to_blind[i] = std::make_pair(GetBlindingKey(&blinding_script), GetBlindingKey(&blinding_script));
}
Expand Down
Loading

0 comments on commit 433a57a

Please sign in to comment.