Skip to content

Commit

Permalink
Merge 1ba24fe into merged_master (Elements PR #1002)
Browse files Browse the repository at this point in the history
This forward-ports the new Taproot sighash but does not fix a couple
22-blocked TODOs related to the MissingDataBehavior enum. Should be fixed
in a followup commit.

One nontrivial change I had to make was feeding the genesis hash to SignTransaction
(the "global" one in script/sign.cpp) so that it could correctly compute
the sighash at signing time.
  • Loading branch information
apoelstra committed Sep 4, 2021
2 parents ff244b0 + 1ba24fe commit 7d1c77f
Show file tree
Hide file tree
Showing 28 changed files with 476 additions and 94 deletions.
1 change: 1 addition & 0 deletions src/bench/verify_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static void VerifyScriptBench(benchmark::Bench& bench)
CDataStream streamVal(SER_NETWORK, PROTOCOL_VERSION);
streamVal << txCredit.vout[0].nValue;
int csuccess = bitcoinconsensus_verify_script_with_amount(
NULL,
txCredit.vout[0].scriptPubKey.data(),
txCredit.vout[0].scriptPubKey.size(),
(const unsigned char*)&streamVal[0], streamVal.size(),
Expand Down
3 changes: 2 additions & 1 deletion src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ class CChainParams

const ChainTxData& TxData() const { return chainTxData; }
// ELEMENTS extra fields:
const uint256 ParentGenesisBlockHash() const { return parentGenesisBlockHash; }
const uint256& ParentGenesisBlockHash() const { return parentGenesisBlockHash; }
const uint256& HashGenesisBlock() const { return consensus.hashGenesisBlock; }
bool anyonecanspend_aremine;
const std::string& ParentBech32HRP() const { return parent_bech32_hrp; }
const std::string& ParentBlech32HRP() const { return parent_blech32_hrp; }
Expand Down
2 changes: 1 addition & 1 deletion src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
for (size_t idx = 0; idx < psbt.inputs.size(); ++idx) {
if (!psbt.inputs[idx].GetUTXO(utxos[idx])) have_all_spent_outputs = false;
}
PrecomputedTransactionData txdata;
PrecomputedTransactionData txdata{Params().HashGenesisBlock()};
if (have_all_spent_outputs) {
txdata.Init(tx, std::move(utxos), true);
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,16 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
}

static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
static const CHashWriter HASHER_TAPTWEAK_ELEMENTS = TaggedHash("TapTweak/elements");

uint256 XOnlyPubKey::ComputeTapTweakHash(const uint256* merkle_root) const
{
if (merkle_root == nullptr) {
// We have no scripts. The actual tweak does not matter, but follow BIP341 here to
// allow for reproducible tweaking.
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata).GetSHA256();
return (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << m_keydata).GetSHA256();
} else {
return (CHashWriter(HASHER_TAPTWEAK) << m_keydata << *merkle_root).GetSHA256();
return (CHashWriter(HASHER_TAPTWEAK_ELEMENTS) << m_keydata << *merkle_root).GetSHA256();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rpc/rawtransaction_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
std::map<int, std::string> input_errors;

bool immature_pegin = ValidateTransactionPeginInputs(mtx, active_chain_tip, input_errors);
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
bool complete = SignTransaction(mtx, keystore, coins, nHashType, Params().HashGenesisBlock(), input_errors);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, immature_pegin, result);
}

Expand Down
18 changes: 12 additions & 6 deletions src/script/bitcoinconsensus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <streams.h>
#include <version.h>

namespace {
Expand Down Expand Up @@ -73,7 +74,8 @@ static bool verify_flags(unsigned int flags)
return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0;
}

static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CConfidentialValue amount,
static int verify_script(const unsigned char *hash_genesis_block,
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CConfidentialValue amount,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
{
Expand All @@ -91,15 +93,18 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
// Regardless of the verification result, the tx did not error.
set_error(err, bitcoinconsensus_ERR_OK);

PrecomputedTransactionData txdata(tx);
auto hash_genesis_block_ = hash_genesis_block ? uint256{hash_genesis_block, 32} : uint256{};
PrecomputedTransactionData txdata(hash_genesis_block_);
txdata.Init(tx, {});
const CScriptWitness* pScriptWitness = (tx.witness.vtxinwit.size() > nIn ? &tx.witness.vtxinwit[nIn].scriptWitness : NULL);
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), pScriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr);
} catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
}
}

int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
int bitcoinconsensus_verify_script_with_amount(const unsigned char *hash_genesis_block,
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *amount, unsigned int amountLen,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
Expand All @@ -109,14 +114,15 @@ int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey
CConfidentialValue am;
stream >> am;

return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
return ::verify_script(hash_genesis_block, scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
} catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
}
}


int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
int bitcoinconsensus_verify_script(const unsigned char *hash_genesis_block,
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err)
{
Expand All @@ -125,7 +131,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i
}

CConfidentialValue am(0);
return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
return ::verify_script(hash_genesis_block, scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err);
}

unsigned int bitcoinconsensus_version()
Expand Down
6 changes: 4 additions & 2 deletions src/script/bitcoinconsensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@ enum
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
/// the additional constraints specified by flags.
/// If not nullptr, err will contain an error/success code for the operation
EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *hash_genesis_block,
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);

EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *hash_genesis_block,
const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *amount, unsigned int amountLen,
const unsigned char *txTo , unsigned int txToLen,
unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err);
Expand Down
105 changes: 87 additions & 18 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,17 @@ class CTransactionSignatureSerializer
}
};

/** Compute the (single) SHA256 of the concatenation of all outpoint flags of a tx. */
template <class T>
uint256 GetOutpointFlagsSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << (unsigned char) ((!txin.assetIssuance.IsNull() << 7) + (txin.m_is_pegin << 6));
}
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all prevouts of a tx. */
template <class T>
uint256 GetPrevoutsSHA256(const T& txTo)
Expand All @@ -1779,7 +1790,8 @@ uint256 GetSequencesSHA256(const T& txTo)
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all txouts of a tx. */
/** Compute the (single) SHA256 of the concatenation of all issuances of a tx. */
// Used for segwitv0/taproot sighash calculation
template <class T>
uint256 GetIssuanceSHA256(const T& txTo)
{
Expand All @@ -1793,6 +1805,34 @@ uint256 GetIssuanceSHA256(const T& txTo)
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all output witnesses
* (rangeproof and surjection proof) in `CTxWitness`*/
// Used in taphash calculation
template <class T>
uint256 GetOutputWitnessesSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& outwit : txTo.witness.vtxoutwit) {
ss << outwit;
}
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all input issuance witnesses
* (vchIssuanceAmountRangeproof and vchInflationKeysRangeproof proof) in `CTxInWitness`*/
// Used in taphash calculation
template <class T>
uint256 GetIssuanceRangeproofsSHA256(const T& txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& inwit : txTo.witness.vtxinwit) {
ss << inwit.vchIssuanceAmountRangeproof;
ss << inwit.vchInflationKeysRangeproof;
}
return ss.GetSHA256();
}

// Compute a (single) SHA256 of the concatenation of all outputs
template <class T>
uint256 GetOutputsSHA256(const T& txTo)
{
Expand All @@ -1803,11 +1843,13 @@ uint256 GetOutputsSHA256(const T& txTo)
return ss.GetSHA256();
}

/** Compute the (single) SHA256 of the concatenation of all amounts spent by a tx. */
uint256 GetSpentAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
/** Compute the (single) SHA256 of the concatenation of all asset and amounts commitments spent by a tx. */
// Elements TapHash only
uint256 GetSpentAssetsAmountsSHA256(const std::vector<CTxOut>& outputs_spent)
{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : outputs_spent) {
ss << txout.nAsset;
ss << txout.nValue;
}
return ss.GetSHA256();
Expand Down Expand Up @@ -1878,24 +1920,29 @@ void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent
m_prevouts_single_hash = GetPrevoutsSHA256(txTo);
m_sequences_single_hash = GetSequencesSHA256(txTo);
m_outputs_single_hash = GetOutputsSHA256(txTo);
m_issuances_single_hash = GetIssuanceSHA256(txTo);
}
if (uses_bip143_segwit) {
hashPrevouts = SHA256Uint256(m_prevouts_single_hash);
hashSequence = SHA256Uint256(m_sequences_single_hash);
hashIssuance = SHA256Uint256(GetIssuanceSHA256(txTo));
hashIssuance = SHA256Uint256(m_issuances_single_hash);
hashOutputs = SHA256Uint256(m_outputs_single_hash);
hashRangeproofs = GetRangeproofsHash(txTo);
m_bip143_segwit_ready = true;
}
if (uses_bip341_taproot) {
m_spent_amounts_single_hash = GetSpentAmountsSHA256(m_spent_outputs);
m_outpoints_flag_single_hash = GetOutpointFlagsSHA256(txTo);
m_spent_asset_amounts_single_hash = GetSpentAssetsAmountsSHA256(m_spent_outputs);
m_issuance_rangeproofs_single_hash = GetIssuanceRangeproofsSHA256(txTo);
m_output_witnesses_single_hash = GetOutputWitnessesSHA256(txTo);
m_spent_scripts_single_hash = GetSpentScriptsSHA256(m_spent_outputs);
m_bip341_taproot_ready = true;
}
}

template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
: PrecomputedTransactionData(uint256{})
{
Init(txTo, {});
}
Expand All @@ -1906,9 +1953,12 @@ template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo,
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);

static const CHashWriter HASHER_TAPSIGHASH = TaggedHash("TapSighash");
const CHashWriter HASHER_TAPLEAF = TaggedHash("TapLeaf");
const CHashWriter HASHER_TAPBRANCH = TaggedHash("TapBranch");
static const CHashWriter HASHER_TAPSIGHASH_ELEMENTS = TaggedHash("TapSighash/elements");
const CHashWriter HASHER_TAPLEAF_ELEMENTS = TaggedHash("TapLeaf/elements");
const CHashWriter HASHER_TAPBRANCH_ELEMENTS = TaggedHash("TapBranch/elements");

PrecomputedTransactionData::PrecomputedTransactionData(const uint256& hash_genesis_block)
: m_tapsighash_hasher(CHashWriter(HASHER_TAPSIGHASH_ELEMENTS) << hash_genesis_block << hash_genesis_block) {}

static bool HandleMissingData(MissingDataBehavior mdb)
{
Expand Down Expand Up @@ -1947,11 +1997,11 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
return HandleMissingData(mdb);
}

CHashWriter ss = HASHER_TAPSIGHASH;
CHashWriter ss = cache.m_tapsighash_hasher;

// Epoch
static constexpr uint8_t EPOCH = 0;
ss << EPOCH;
// no epoch in elements taphash
// static constexpr uint8_t EPOCH = 0;
// ss << EPOCH;

// Hash type
const uint8_t output_type = (hash_type == SIGHASH_DEFAULT) ? SIGHASH_ALL : (hash_type & SIGHASH_OUTPUT_MASK); // Default (no sighash byte) is equivalent to SIGHASH_ALL
Expand All @@ -1963,37 +2013,56 @@ bool SignatureHashSchnorr(uint256& hash_out, const ScriptExecutionData& execdata
ss << tx_to.nVersion;
ss << tx_to.nLockTime;
if (input_type != SIGHASH_ANYONECANPAY) {
ss << cache.m_outpoints_flag_single_hash;
ss << cache.m_prevouts_single_hash;
ss << cache.m_spent_amounts_single_hash;
ss << cache.m_spent_asset_amounts_single_hash;
ss << cache.m_spent_scripts_single_hash;
ss << cache.m_sequences_single_hash;
ss << cache.m_issuances_single_hash;
ss << cache.m_issuance_rangeproofs_single_hash;
}
if (output_type == SIGHASH_ALL) {
ss << cache.m_outputs_single_hash;
ss << cache.m_output_witnesses_single_hash;
}

// Data about the input/prevout being spent
assert(execdata.m_annex_init);
const bool have_annex = execdata.m_annex_present;
const uint8_t spend_type = (ext_flag << 1) + (have_annex ? 1 : 0); // The low bit indicates whether an annex is present.
ss << spend_type;
if (input_type == SIGHASH_ANYONECANPAY) {
ss << (unsigned char) ((!tx_to.vin[in_pos].assetIssuance.IsNull() << 7) + (tx_to.vin[in_pos].m_is_pegin << 6));
ss << tx_to.vin[in_pos].prevout;
ss << cache.m_spent_outputs[in_pos];
ss << cache.m_spent_outputs[in_pos].nAsset;
ss << cache.m_spent_outputs[in_pos].nValue;
ss << cache.m_spent_outputs[in_pos].scriptPubKey;
ss << tx_to.vin[in_pos].nSequence;
if (tx_to.vin[in_pos].assetIssuance.IsNull()) {
ss << (unsigned char)0;
} else {
ss << tx_to.vin[in_pos].assetIssuance;

CHashWriter sha_single_input_issuance_witness(SER_GETHASH, 0);
sha_single_input_issuance_witness << tx_to.witness.vtxinwit[in_pos].vchIssuanceAmountRangeproof;
sha_single_input_issuance_witness << tx_to.witness.vtxinwit[in_pos].vchInflationKeysRangeproof;
ss << sha_single_input_issuance_witness.GetSHA256();
}
} else {
ss << in_pos;
}
if (have_annex) {
ss << execdata.m_annex_hash;
}

// Data about the output (if only one).
if (output_type == SIGHASH_SINGLE) {
if (in_pos >= tx_to.vout.size()) return false;
CHashWriter sha_single_output(SER_GETHASH, 0);
sha_single_output << tx_to.vout[in_pos];
ss << sha_single_output.GetSHA256();

CHashWriter sha_single_output_witness(SER_GETHASH, 0);
sha_single_output_witness << tx_to.witness.vtxoutwit[in_pos];
ss << sha_single_output_witness.GetSHA256();
}

// Additional data for BIP 342 signatures
Expand Down Expand Up @@ -2310,15 +2379,15 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS

uint256 ComputeTapleafHash(uint8_t leaf_version, const CScript& script)
{
return (CHashWriter(HASHER_TAPLEAF) << leaf_version << script).GetSHA256();
return (CHashWriter(HASHER_TAPLEAF_ELEMENTS) << leaf_version << script).GetSHA256();
}

uint256 ComputeTaprootMerkleRoot(Span<const unsigned char> control, const uint256& tapleaf_hash)
{
const int path_len = (control.size() - TAPROOT_CONTROL_BASE_SIZE) / TAPROOT_CONTROL_NODE_SIZE;
uint256 k = tapleaf_hash;
for (int i = 0; i < path_len; ++i) {
CHashWriter ss_branch{HASHER_TAPBRANCH};
CHashWriter ss_branch = CHashWriter{HASHER_TAPBRANCH_ELEMENTS};
Span<const unsigned char> node(control.data() + TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * i, TAPROOT_CONTROL_NODE_SIZE);
if (std::lexicographical_compare(k.begin(), k.end(), node.begin(), node.end())) {
ss_branch << k << node;
Expand Down
Loading

0 comments on commit 7d1c77f

Please sign in to comment.