Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion depends/packages/qt.mk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package=qt
$(package)_version=5.15.3
$(package)_download_path=https://download.qt.io/official_releases/qt/5.15/$($(package)_version)/submodules
$(package)_download_path=https://download.qt.io/archive/qt/5.15/$($(package)_version)/submodules
$(package)_suffix=everywhere-opensource-src-$($(package)_version).tar.xz
$(package)_file_name=qtbase-$($(package)_suffix)
$(package)_sha256_hash=26394ec9375d52c1592bd7b689b1619c6b8dbe9b6f91fdd5c355589787f3a0b6
Expand Down
2 changes: 1 addition & 1 deletion doc/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) |
| Python (tests) | | [3.6](https://www.python.org/downloads) | | | |
| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | |
| Qt | [5.15.3](https://download.qt.io/official_releases/qt/) | [5.9.5](https://github.com/bitcoin/bitcoin/issues/20104) | No | | |
| Qt | [5.15.3](https://download.qt.io/archive/qt/) | [5.9.5](https://github.com/bitcoin/bitcoin/issues/20104) | No | | |
| SQLite | [3.32.1](https://sqlite.org/download.html) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) | | | |
| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
| systemtap ([tracing](tracing.md))| [4.5](https://sourceware.org/systemtap/ftp/releases/) | | | | |
Expand Down
19 changes: 14 additions & 5 deletions src/blind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,12 @@ bool SurjectOutput(CTxOutWitness& txoutwit, const std::vector<secp256k1_fixed_as
// with more than 256 inputs. The Elements verification code will always try to give
// secp-zkp the complete list of inputs, and if this exceeds 256 then surjectionproof_verify
// will always return false, so there is no way to work around this situation at signing time
if (surjection_targets.size() > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) {
if (surjection_targets.empty() || surjection_targets.size() > SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS) {
// We must return false here to avoid triggering an assertion within
// secp256k1_surjectionproof_initialize on the next line.
// secp256k1_surjectionproof_initialize on the next line: the
// cryptographic API requires a non-empty set of surjection targets,
// and the raw-blinding path can reach us with an empty vector
// (zero-input tx with multiple blindable outputs).
return false;
}
// Find correlation between asset tag and listed input tags
Expand Down Expand Up @@ -546,7 +549,9 @@ int BlindTransaction(std::vector<uint256 >& input_value_blinding_factors, const

// Generate rangeproof, no script committed for issuances
bool rangeresult = GenerateRangeproof((nPseudo ? txinwit.vchInflationKeysRangeproof : txinwit.vchIssuanceAmountRangeproof), value_blindptrs, nonce, amount, CScript(), value_commit, asset_gen, asset, asset_blindptrs);
assert(rangeresult);
if (!rangeresult) {
return -1;
}

// Successfully blinded this issuance
num_blinded++;
Expand Down Expand Up @@ -621,9 +626,13 @@ int BlindTransaction(std::vector<uint256 >& input_value_blinding_factors, const

// Generate rangeproof
bool rangeresult = GenerateRangeproof(txoutwit.vchRangeproof, value_blindptrs, nonce, amount, out.scriptPubKey, value_commit, asset_gen, asset, asset_blindptrs);
assert(rangeresult);
if (!rangeresult) {
return -1;
}

// Create surjection proof for this output
// Failed surjection proof is a foreseeable condition
// (no suitable input asset to prove against) and is reported to the
// caller via the returned count. See naive_blinding_test.
if (!SurjectOutput(txoutwit, surjection_targets, target_asset_generators, target_asset_blinders, asset_blindptrs, asset_gen, asset)) {
continue;
}
Expand Down
56 changes: 50 additions & 6 deletions src/blindpsbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ std::string GetBlindingStatusError(const BlindingStatus& status)
return "Unable to create an asset surjection proof";
case BlindingStatus::NO_BLIND_OUTPUTS:
return "Transaction has blind inputs belonging to this blinder but does not have outputs to blind";
case BlindingStatus::RANGEPROOF_UNABLE:
return "Unable to create a value rangeproof for an output";
case BlindingStatus::INVALID_AMOUNT:
return "Zero-valued output to a spendable script cannot be blinded";
}
assert(false);
}
Expand All @@ -53,10 +57,17 @@ bool CreateAssetSurjectionProof(std::vector<unsigned char>& output_proof, const
}
// Using the input chosen, build proof
ret = secp256k1_surjectionproof_generate(secp256k1_blind_context, &proof, &ephemeral_input_tags[0], ephemeral_input_tags.size(), &output_asset_tag, input_index, input_asset_blinders[input_index].begin(), output_asset_blinder.begin());
assert(ret == 1);
if (ret != 1) {
// Attacker-selected tags/generators without a known discrete-log
// relationship cause generation to fail; this must be a recoverable
// PSET error, not a process abort.
return false;
}
// Double-check answer
ret = secp256k1_surjectionproof_verify(secp256k1_blind_context, &proof, &ephemeral_input_tags[0], ephemeral_input_tags.size(), &output_asset_tag);
assert(ret == 1);
if (ret != 1) {
return false;
}

// Serialize into output witness structure
size_t output_len = secp256k1_surjectionproof_serialized_size(secp256k1_blind_context, &proof);
Expand Down Expand Up @@ -190,7 +201,11 @@ bool CreateBlindAssetProof(std::vector<unsigned char>& assetproof, const CAsset&

bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value, const std::vector<unsigned char>& proof, const CConfidentialAsset& conf_asset)
{
if (conf_value.IsNull() || conf_asset.IsNull()) {
// The value and asset must be genuine commitments (33-byte, PrefixA/B)
// before their buffers are handed to libsecp256k1, which consumes exactly
// 33 serialized bytes. An explicit 9-byte value (or a null field) must not
// reach the parser, which would otherwise read out of bounds.
if (!conf_value.IsCommitment() || !conf_asset.IsCommitment()) {
return false;
}

Expand All @@ -209,7 +224,11 @@ bool VerifyBlindValueProof(CAmount value, const CConfidentialValue& conf_value,
if (secp256k1_rangeproof_verify(secp256k1_blind_context, &min_value, &max_value, &value_commit, proof.data(), proof.size(), /* extra_commit */ nullptr, /* extra_commit_len */ 0, &gen) == 0) {
return false;
}
return min_value == (uint64_t)value;
// A range-membership proof is only meaningful as an equality proof if the
// proven interval collapses to the claimed amount. Comparing solely the
// lower bound would accept a proof whose committed value is larger than
// the displayed amount. Require both bounds to equal `value`.
return min_value == (uint64_t)value && max_value == (uint64_t)value;
}

BlindProofResult VerifyBlindProofs(const PSBTOutput& o) {
Expand Down Expand Up @@ -498,9 +517,23 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
continue;
}

// A rangeproof over a spendable script uses min_value = 1, so a zero
// amount cannot be proven. Reject.
if (*output.amount == 0 && !output.script->IsUnspendable()) {
return BlindingStatus::INVALID_AMOUNT;
}

// Check this is our output to blind
if (output.m_blinder_index == std::nullopt || our_input_data.count(*output.m_blinder_index) == 0) continue;

// PSET v0 does not require an output amount (it is only enforced for
// m_psbt_version >= 2), so a crafted v0 PSET can reach the blinding
// loop with output.amount == nullopt. Dereferencing it is undefined
// behaviour. Refuse to blind such an output.
if (output.amount == std::nullopt) {
return BlindingStatus::INVALID_BLINDER;
}

// Things we are going to stuff into the PSBTOutput if everything is successful
CConfidentialValue value_commitment;
CConfidentialAsset asset_commitment;
Expand Down Expand Up @@ -556,16 +589,27 @@ BlindingStatus BlindPSBT(PartiallySignedTransaction& psbt, std::map<uint32_t, st
CreateValueCommitment(value_commitment, value_commit, value_blinder, asset_generator, *output.amount);

// Generate rangproof nonce
if (!output.m_blinding_pubkey.IsFullyValid()) {
// An attacker-controlled (off-curve) blinding pubkey would otherwise
// reach CKey::ECDH, whose only validation is an assert on the peer
// key, aborting the process. The non-PSET path (blind.cpp) requires
// IsFullyValid() before ECDH; mirror it here.
return BlindingStatus::INVALID_BLINDER;
}
uint256 nonce = GenerateRangeproofECDHKey(ecdh_key, output.m_blinding_pubkey);

// Generate rangeproof
bool rangeresult = CreateValueRangeProof(rangeproof, value_blinder, nonce, *output.amount, *output.script, value_commit, asset_generator, asset, asset_blinder);
assert(rangeresult);
if (!rangeresult) {
return BlindingStatus::RANGEPROOF_UNABLE;
}

// Create explicit value rangeproof
std::vector<unsigned char> blind_value_proof;
rangeresult = CreateBlindValueProof(blind_value_proof, value_blinder, *output.amount, value_commit, asset_generator);
assert(rangeresult);
if (!rangeresult) {
return BlindingStatus::RANGEPROOF_UNABLE;
}

// Create surjection proof for this output
if (!CreateAssetSurjectionProof(asp, fixed_input_tags, ephemeral_input_tags, input_asset_blinders, asset_blinder, asset_generator, asset)) {
Expand Down
2 changes: 2 additions & 0 deletions src/blindpsbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ enum class BlindingStatus
INVALID_BLINDER,
ASP_UNABLE,
NO_BLIND_OUTPUTS,
RANGEPROOF_UNABLE,
INVALID_AMOUNT,
};

enum class BlindProofResult {
Expand Down
3 changes: 3 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ class CChainParams
bool GetAcceptDiscountCT() const { return accept_discount_ct; }
bool GetCreateDiscountCT() const { return create_discount_ct; }

// ELEMENTS: Elements adds classes with their own members so the base pointer needs a virtual destructor.
virtual ~CChainParams() = default;

protected:
CChainParams() {}

Expand Down
7 changes: 5 additions & 2 deletions src/dynafed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ bool NextBlockIsParameterTransition(const CBlockIndex* pindexPrev, const Consens
}
std::map<uint256, uint32_t> vote_tally;
assert(next_height >= consensus.dynamic_epoch_length);
// Require at least four-fifths of the epoch's votes. (epoch_length*4)/5
// floor-divides, under-approximating the 80% threshold for epoch lengths
// not divisible by 5; N - N/5 is the overflow-safe ceiling of N*4/5.
const uint32_t threshold = consensus.dynamic_epoch_length - consensus.dynamic_epoch_length / 5;
for (int32_t height = next_height - 1; height >= (int32_t)(next_height - consensus.dynamic_epoch_length); --height) {
const CBlockIndex* p_epoch_walk = pindexPrev->GetAncestor(height);
assert(p_epoch_walk);
Expand All @@ -25,8 +29,7 @@ bool NextBlockIsParameterTransition(const CBlockIndex* pindexPrev, const Consens
const uint256 proposal_root = proposal.CalculateRoot();
vote_tally[proposal_root]++;
// Short-circuit once 4/5 threshold is reached
if (!proposal_root.IsNull() && vote_tally[proposal_root] >=
(consensus.dynamic_epoch_length*4)/5) {
if (!proposal_root.IsNull() && vote_tally[proposal_root] >= threshold) {
winning_entry = proposal;
return true;
}
Expand Down
7 changes: 7 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,13 @@ bool AppInitParameterInteraction(const ArgsManager& args)
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
}

if (chainparams.GetConsensus().has_parent_chain && !chainparams.GetConsensus().ParentChainHasPow()) {
LogPrintf("This chain is configured with a signed-blocks parent chain. "
"Peg-ins referencing a parent block that has activated dynamic "
"federations will be rejected: such headers cannot be "
"authenticated. See doc/ for details.\n");
}

// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that happens
if (args.IsArgSet("-blockmintxfee")) {
Expand Down
84 changes: 54 additions & 30 deletions src/pegins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,39 +554,63 @@ bool DecomposePeginWitness(const CScriptWitness& witness, CAmount& value, CAsset

if (stack.size() != 6) return false;

CDataStream stream(stack[0], SER_NETWORK, PROTOCOL_VERSION);
stream >> value;

CAsset tmp_asset(stack[1]);
asset = tmp_asset;

uint256 gh(stack[2]);
genesis_hash = gh;

CScript s(stack[3].begin(), stack[3].end());
claim_script = s;
// Fixed-width fields must be size-checked before construction: the
// base_blob vector constructor asserts on a length mismatch
// (uint256.cpp:15), and an assert is not catchable by the try below.
// CAsset delegates to the same constructor.
if (stack[1].size() != 32) return false; // asset
if (stack[2].size() != 32) return false; // parent genesis hash

// Decompose into locals so a failure part-way through cannot leave the
// caller's out-parameters partially populated.
CAmount tmp_value{0};
CAsset tmp_asset;
uint256 tmp_genesis_hash;
CScript tmp_claim_script;
std::variant<std::monostate, Sidechain::Bitcoin::CTransactionRef, CTransactionRef> tmp_tx;
std::variant<std::monostate, Sidechain::Bitcoin::CMerkleBlock, CMerkleBlock> tmp_merkle_block;

CDataStream ss_tx(stack[4], SER_NETWORK, PROTOCOL_VERSION);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CTransactionRef btc_tx;
ss_tx >> btc_tx;
tx = btc_tx;
} else {
CTransactionRef elem_tx;
ss_tx >> elem_tx;
tx = elem_tx;
}
try {
CDataStream stream(stack[0], SER_NETWORK, PROTOCOL_VERSION);
stream >> tmp_value;

tmp_asset = CAsset(stack[1]);
tmp_genesis_hash = uint256(stack[2]);
tmp_claim_script = CScript(stack[3].begin(), stack[3].end());

CDataStream ss_tx(stack[4], SER_NETWORK, PROTOCOL_VERSION);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CTransactionRef btc_tx;
ss_tx >> btc_tx;
tmp_tx = btc_tx;
} else {
CTransactionRef elem_tx;
ss_tx >> elem_tx;
tmp_tx = elem_tx;
}

CDataStream ss_proof(stack[5], SER_NETWORK, PROTOCOL_VERSION);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CMerkleBlock tx_proof;
ss_proof >> tx_proof;
merkle_block = tx_proof;
} else {
CMerkleBlock tx_proof;
ss_proof >> tx_proof;
merkle_block = tx_proof;
CDataStream ss_proof(stack[5], SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CMerkleBlock tx_proof;
ss_proof >> tx_proof;
tmp_merkle_block = tx_proof;
} else {
CMerkleBlock tx_proof;
ss_proof >> tx_proof;
tmp_merkle_block = tx_proof;
}
} catch (const std::exception&) {
// Malformed encoding. Report failure rather than propagating, so that
// the bool return means what the signature implies. Callers such as
// PartiallySignedTransaction::SetupFromTx have no exception handling.
return false;
}

value = tmp_value;
asset = tmp_asset;
genesis_hash = tmp_genesis_hash;
claim_script = tmp_claim_script;
tx = std::move(tmp_tx);
merkle_block = std::move(tmp_merkle_block);
return true;
}
10 changes: 10 additions & 0 deletions src/primitives/pak.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,13 @@ bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist, const uint256
}
return true;
}

bool HasConfidentialPegoutOutput(const CTransaction& tx, const uint256& parent_gen_hash)
{
for (const auto& txout : tx.vout) {
if (txout.scriptPubKey.IsPegoutScript(parent_gen_hash) && !txout.nAsset.IsExplicit()) {
return true;
}
}
return false;
}
2 changes: 2 additions & 0 deletions src/primitives/pak.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ bool IsPAKValidOutput(const CTxOut& txout, const CPAKList& paklist, const uint25

bool IsPAKValidTx(const CTransaction& tx, const CPAKList& paklist, const uint256& parent_gen_hash, const CAsset& peg_asset);

bool HasConfidentialPegoutOutput(const CTransaction& tx, const uint256& parent_gen_hash);

#endif // BITCOIN_PRIMITIVES_PAK_H
15 changes: 12 additions & 3 deletions src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,12 +868,21 @@ void PartiallySignedTransaction::SetupFromTx(const CMutableTransaction& tx)
}
}
// Peg-in things
if (txin.m_is_pegin) {
if (txin.m_is_pegin && i < tx.witness.vtxinwit.size()) {
CAmount peg_in_value;
CAsset asset;
if (DecomposePeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, peg_in_value, asset, input.m_peg_in_genesis_hash, input.m_peg_in_claim_script, input.m_peg_in_tx, input.m_peg_in_txout_proof)) {
uint256 genesis_hash;
CScript claim_script;
std::variant<std::monostate, Sidechain::Bitcoin::CTransactionRef, CTransactionRef> peg_in_tx;
std::variant<std::monostate, Sidechain::Bitcoin::CMerkleBlock, CMerkleBlock> txout_proof;
if (DecomposePeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, peg_in_value, asset,
genesis_hash, claim_script, peg_in_tx, txout_proof)
&& asset == Params().GetConsensus().pegged_asset) {
input.m_peg_in_value = peg_in_value;
assert(asset == Params().GetConsensus().pegged_asset);
input.m_peg_in_genesis_hash = genesis_hash;
input.m_peg_in_claim_script = claim_script;
input.m_peg_in_tx = peg_in_tx;
input.m_peg_in_txout_proof = txout_proof;
}
}
}
Expand Down
Loading
Loading