ZIP 244 transaction digests

This commit is contained in:
Jack Grigg 2021-06-11 21:34:15 +01:00
parent bad7f7eadb
commit bd1fd2eaca
6 changed files with 133 additions and 2 deletions

View File

@ -9,6 +9,8 @@
#include "tinyformat.h" #include "tinyformat.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
#include <rust/transaction.h>
SaplingBundle::SaplingBundle( SaplingBundle::SaplingBundle(
const std::vector<SpendDescription>& vShieldedSpend, const std::vector<SpendDescription>& vShieldedSpend,
const std::vector<OutputDescription>& vShieldedOutput, const std::vector<OutputDescription>& vShieldedOutput,
@ -138,12 +140,39 @@ CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.n
uint256 CMutableTransaction::GetHash() const uint256 CMutableTransaction::GetHash() const
{ {
return SerializeHash(*this); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
uint256 hash;
assert(zcash_transaction_digests(
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
hash.begin(),
nullptr));
return hash;
}
uint256 CMutableTransaction::GetAuthDigest() const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
uint256 authDigest;
assert(zcash_transaction_digests(
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
nullptr,
authDigest.begin()));
return authDigest;
} }
void CTransaction::UpdateHash() const void CTransaction::UpdateHash() const
{ {
*const_cast<uint256*>(&hash) = SerializeHash(*this); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
assert(zcash_transaction_digests(
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
const_cast<uint256*>(&hash)->begin(),
const_cast<uint256*>(&authDigest)->begin()));
} }
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION),

View File

@ -714,6 +714,8 @@ private:
/** Memory only. */ /** Memory only. */
const uint256 hash; const uint256 hash;
/** Memory only. */
const uint256 authDigest;
void UpdateHash() const; void UpdateHash() const;
protected: protected:
@ -899,6 +901,15 @@ public:
return hash; return hash;
} }
/**
* Returns the authorizing data commitment for this transaction.
*
* For v1-v4 transactions, this returns the null hash (i.e. all-zeroes).
*/
const uint256& GetAuthDigest() const {
return authDigest;
}
uint32_t GetHeader() const { uint32_t GetHeader() const {
// When serializing v1 and v2, the 4 byte header is nVersion // When serializing v1 and v2, the 4 byte header is nVersion
uint32_t header = this->nVersion; uint32_t header = this->nVersion;
@ -1093,6 +1104,14 @@ struct CMutableTransaction
* fly, as opposed to GetHash() in CTransaction, which uses a cached result. * fly, as opposed to GetHash() in CTransaction, which uses a cached result.
*/ */
uint256 GetHash() const; uint256 GetHash() const;
/** Compute the authentication digest of this CMutableTransaction. This is
* computed on the fly, as opposed to GetAuthDigest() in CTransaction, which
* uses a cached result.
*
* For v1-v4 transactions, this returns the null hash (i.e. all-zeroes).
*/
uint256 GetAuthDigest() const;
}; };
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H #endif // BITCOIN_PRIMITIVES_TRANSACTION_H

View File

@ -0,0 +1,31 @@
// Copyright (c) 2020 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#ifndef ZCASH_RUST_INCLUDE_RUST_TRANSACTION_H
#define ZCASH_RUST_INCLUDE_RUST_TRANSACTION_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Calculates identifying and authorizing digests for the given transaction.
///
/// If either `txid_ret` or `authDigest_ret` is `nullptr`, the corresponding
/// digest will not be calculated.
///
/// Returns `false` if the transaction is invalid.
bool zcash_transaction_digests(
const unsigned char* txBytes,
size_t txBytes_len,
unsigned char* txid_ret,
unsigned char* authDigest_ret);
#ifdef __cplusplus
}
#endif
#endif // ZCASH_RUST_INCLUDE_RUST_TRANSACTION_H

View File

@ -70,6 +70,7 @@ mod streams_ffi;
mod tracing_ffi; mod tracing_ffi;
mod orchard_ffi; mod orchard_ffi;
mod transaction_ffi;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -0,0 +1,47 @@
use std::slice;
use libc::{c_uchar, size_t};
use tracing::error;
use zcash_primitives::{
consensus::BranchId,
transaction::{Transaction, TxVersion},
};
/// Calculates identifying and authorizing digests for the given transaction.
///
/// If either `txid_ret` or `auth_digest_ret` is `nullptr`, the corresponding digest will
/// not be calculated.
///
/// Returns `false` if the transaction is invalid.
#[no_mangle]
pub extern "C" fn zcash_transaction_digests(
tx_bytes: *const c_uchar,
tx_bytes_len: size_t,
txid_ret: *mut [u8; 32],
auth_digest_ret: *mut [u8; 32],
) -> bool {
let tx_bytes = unsafe { slice::from_raw_parts(tx_bytes, tx_bytes_len) };
// We use a placeholder branch ID here, since it is not used for anything.
let tx = match Transaction::read(tx_bytes, BranchId::Canopy) {
Ok(tx) => tx,
Err(e) => {
error!("Failed to parse transaction: {}", e);
return false;
}
};
if let Some(txid_ret) = unsafe { txid_ret.as_mut() } {
*txid_ret = *tx.txid().as_ref();
}
if let Some(auth_digest_ret) = unsafe { auth_digest_ret.as_mut() } {
match tx.version() {
// Pre-NU5 transaction formats don't have auth digests; leave it empty so we
// don't need to know the correct consensus branch ID for them.
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling => (),
_ => auth_digest_ret.copy_from_slice(tx.auth_commitment().as_bytes()),
}
}
true
}

View File

@ -868,6 +868,10 @@ BOOST_AUTO_TEST_CASE(TxV5)
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << tx; ss << tx;
BOOST_CHECK_EQUAL(HexStr(ss.begin(), ss.end()), transaction); BOOST_CHECK_EQUAL(HexStr(ss.begin(), ss.end()), transaction);
// ZIP 244: Check the transaction digests.
BOOST_CHECK_EQUAL(tx.GetHash().GetHex(), test[1].getValStr());
BOOST_CHECK_EQUAL(tx.GetAuthDigest().GetHex(), test[2].getValStr());
} }
} }