ZIP 244 transaction digests
This commit is contained in:
parent
bad7f7eadb
commit
bd1fd2eaca
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue