Auto merge of #5202 - str4d:5022-tx-v5, r=str4d
v5 transaction format Includes a new wrapper that enables passing C++ streams across to Rust. Closes zcash/zcash#5022.
This commit is contained in:
commit
bad7f7eadb
|
@ -775,6 +775,7 @@ dependencies = [
|
|||
"blake2b_simd",
|
||||
"blake2s_simd",
|
||||
"bls12_381",
|
||||
"byteorder",
|
||||
"ed25519-zebra",
|
||||
"group",
|
||||
"hyper",
|
||||
|
@ -783,6 +784,8 @@ dependencies = [
|
|||
"libc",
|
||||
"metrics",
|
||||
"metrics-exporter-prometheus",
|
||||
"nonempty",
|
||||
"orchard",
|
||||
"rand_core 0.6.2",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
|
|
|
@ -24,9 +24,12 @@ bellman = "0.10"
|
|||
blake2b_simd = "0.5"
|
||||
blake2s_simd = "0.5"
|
||||
bls12_381 = "0.5"
|
||||
byteorder = "1"
|
||||
group = "0.10"
|
||||
libc = "0.2"
|
||||
jubjub = "0.7"
|
||||
nonempty = "0.6"
|
||||
orchard = "0.0"
|
||||
subtle = "2.2"
|
||||
rand_core = "0.6"
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -120,6 +120,12 @@ Files: depends/sources/utfcpp-*.tar.gz
|
|||
Copyright: 2006 Nemanja Trifunovic
|
||||
License: Boost-Software-License-1.0
|
||||
|
||||
Files: depends/*/vendored-sources/halo2/*
|
||||
depends/*/vendored-sources/orchard/*
|
||||
depends/*/vendored-sources/pasta_curves/*
|
||||
Copyright: 2020 The Electric Coin Company
|
||||
License: Bootstrap-Open-Source-Licence-1.0
|
||||
|
||||
Files: src/crypto/ctaes/*
|
||||
Copyright: Copyright (c) 2016 Pieter Wuille
|
||||
License: Expat
|
||||
|
@ -1151,3 +1157,181 @@ License: Expat-with-advertising-clause
|
|||
their institutions shall not be used in advertising or otherwise to
|
||||
promote the sale, use or other dealings in this Software without
|
||||
prior written authorization from the authors.
|
||||
|
||||
License: Bootstrap-Open-Source-Licence-1.0
|
||||
=======================================================
|
||||
Bootstrap Open Source Licence ("BOSL") v. 1.0
|
||||
=======================================================
|
||||
This Bootstrap Open Source Licence (the "License") applies to any original work
|
||||
of authorship (the "Original Work") whose owner (the "Licensor") has placed the
|
||||
following licensing notice adjacent to the copyright notice for the Original
|
||||
Work:
|
||||
.
|
||||
*Licensed under the Bootstrap Open Source Licence version 1.0*
|
||||
.
|
||||
1. **Grant of Copyright License.** Licensor grants You a worldwide,
|
||||
royalty-free, non-exclusive, sublicensable license, for the duration of the
|
||||
copyright in the Original Work, to do the following:
|
||||
.
|
||||
a. to reproduce the Original Work in copies, either alone or as part of
|
||||
a collective work;
|
||||
.
|
||||
b. to translate, adapt, alter, transform, modify, or arrange the
|
||||
Original Work, thereby creating derivative works ("Derivative Works")
|
||||
based upon the Original Work;
|
||||
.
|
||||
c. to distribute or communicate copies of the Original Work and
|
||||
Derivative Works to the public, provided that prior to any such
|
||||
distribution or communication You first place a machine-readable copy
|
||||
of the Source Code of the Original Work and such Derivative Works that
|
||||
You intend to distribute or communicate in an information repository
|
||||
reasonably calculated to permit inexpensive and convenient access
|
||||
thereto by the public (“Information Repository”) for as long as You
|
||||
continue to distribute or communicate said copies, accompanied by an
|
||||
irrevocable offer to license said copies to the public free of charge
|
||||
under this License, said offer valid starting no later than 12 months
|
||||
after You first distribute or communicate said copies;
|
||||
.
|
||||
d. to perform the Original Work publicly; and
|
||||
.
|
||||
e. to display the Original Work publicly.
|
||||
.
|
||||
2. **Grant of Patent License.** Licensor grants You a worldwide, royalty-free,
|
||||
non-exclusive, sublicensable license, under patent claims owned or controlled
|
||||
by the Licensor that are embodied in the Original Work as furnished by the
|
||||
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
|
||||
have made, and import the Original Work and Derivative Works.
|
||||
.
|
||||
3. **Grant of Source Code License.** The "Source Code" for a work means the
|
||||
preferred form of the work for making modifications to it and all available
|
||||
documentation describing how to modify the work. Licensor agrees to provide a
|
||||
machine-readable copy of the Source Code of the Original Work along with each
|
||||
copy of the Original Work that Licensor distributes. Licensor reserves the
|
||||
right to satisfy this obligation by placing a machine-readable copy of said
|
||||
Source Code in an Information Repository for as long as Licensor continues to
|
||||
distribute the Original Work.
|
||||
.
|
||||
4. **Exclusions From License Grant.** Neither the names of Licensor, nor the
|
||||
names of any contributors to the Original Work, nor any of their trademarks or
|
||||
service marks, may be used to endorse or promote products derived from this
|
||||
Original Work without express prior permission of the Licensor. Except as
|
||||
expressly stated herein, nothing in this License grants any license to
|
||||
Licensor's trademarks, copyrights, patents, trade secrets or any other
|
||||
intellectual property. No patent license is granted to make, use, sell, offer
|
||||
for sale, have made, or import embodiments of any patent claims other than the
|
||||
licensed claims defined in Section 2. No license is granted to the trademarks
|
||||
of Licensor even if such marks are included in the Original Work. Nothing in
|
||||
this License shall be interpreted to prohibit Licensor from licensing under
|
||||
terms different from this License any Original Work that Licensor otherwise
|
||||
would have a right to license.
|
||||
.
|
||||
5. **External Deployment.** The term "External Deployment" means the use,
|
||||
distribution, or communication of the Original Work or Derivative Works in any
|
||||
way such that the Original Work or Derivative Works may be used by anyone other
|
||||
than You, whether those works are distributed or communicated to those persons
|
||||
or made available as an application intended for use over a network. As an
|
||||
express condition for the grants of license hereunder, You must treat any
|
||||
External Deployment by You of the Original Work or a Derivative Work as a
|
||||
distribution under section 1(c).
|
||||
.
|
||||
6. **Attribution Rights.** You must retain, in the Source Code of any
|
||||
Derivative Works that You create, all copyright, patent, or trademark notices
|
||||
from the Source Code of the Original Work, as well as any notices of licensing
|
||||
and any descriptive text identified therein as an "Attribution Notice." You
|
||||
must cause the Source Code for any Derivative Works that You create to carry a
|
||||
prominent Attribution Notice reasonably calculated to inform recipients that
|
||||
You have modified the Original Work.
|
||||
.
|
||||
7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants
|
||||
that the copyright in and to the Original Work and the patent rights granted
|
||||
herein by Licensor are owned by the Licensor or are sublicensed to You under
|
||||
the terms of this License with the permission of the contributor(s) of those
|
||||
copyrights and patent rights. Except as expressly stated in the immediately
|
||||
preceding sentence, the Original Work is provided under this License on an "AS
|
||||
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
|
||||
limitation, the warranties of non-infringement, merchantability or fitness for
|
||||
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS
|
||||
WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
|
||||
License. No license to the Original Work is granted by this License except
|
||||
under this disclaimer.
|
||||
.
|
||||
8. **Limitation of Liability.** Under no circumstances and under no legal
|
||||
theory, whether in tort (including negligence), contract, or otherwise, shall
|
||||
the Licensor be liable to anyone for any indirect, special, incidental, or
|
||||
consequential damages of any character arising as a result of this License or
|
||||
the use of the Original Work including, without limitation, damages for loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all other
|
||||
commercial damages or losses. This limitation of liability shall not apply to
|
||||
the extent applicable law prohibits such limitation.
|
||||
.
|
||||
9. **Acceptance and Termination.** If, at any time, You expressly assented to
|
||||
this License, that assent indicates your clear and irrevocable acceptance of
|
||||
this License and all of its terms and conditions. If You distribute or
|
||||
communicate copies of the Original Work or a Derivative Work, You must make a
|
||||
reasonable effort under the circumstances to obtain the express assent of
|
||||
recipients to the terms of this License. This License conditions your rights to
|
||||
undertake the activities listed in Section 1, including your right to create
|
||||
Derivative Works based upon the Original Work, and doing so without honoring
|
||||
these terms and conditions is prohibited by copyright law and international
|
||||
treaty. Nothing in this License is intended to affect copyright exceptions and
|
||||
limitations (including 'fair use' or 'fair dealing'). This License shall
|
||||
terminate immediately and You may no longer exercise any of the rights granted
|
||||
to You by this License upon your failure to honor the conditions in Section
|
||||
1(c).
|
||||
.
|
||||
10. **Termination for Patent Action.** This License shall terminate
|
||||
automatically and You may no longer exercise any of the rights granted to You
|
||||
by this License as of the date You commence an action, including a cross-claim
|
||||
or counterclaim, against Licensor or any licensee alleging that the Original
|
||||
Work infringes a patent. This termination provision shall not apply for an
|
||||
action alleging patent infringement by combinations of the Original Work with
|
||||
other software or hardware.
|
||||
.
|
||||
11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to
|
||||
this License may be brought only in the courts of a jurisdiction wherein the
|
||||
Licensor resides or in which Licensor conducts its primary business, and under
|
||||
the laws of that jurisdiction excluding its conflict-of-law provisions. The
|
||||
application of the United Nations Convention on Contracts for the International
|
||||
Sale of Goods is expressly excluded. Any use of the Original Work outside the
|
||||
scope of this License or after its termination shall be subject to the
|
||||
requirements and penalties of copyright or patent law in the appropriate
|
||||
jurisdiction. This section shall survive the termination of this License.
|
||||
.
|
||||
12. **Attorneys' Fees.** In any action to enforce the terms of this License or
|
||||
seeking damages relating thereto, the prevailing party shall be entitled to
|
||||
recover its costs and expenses, including, without limitation, reasonable
|
||||
attorneys' fees and costs incurred in connection with such action, including
|
||||
any appeal of such action. This section shall survive the termination of this
|
||||
License.
|
||||
.
|
||||
13. **Miscellaneous.** If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent necessary to
|
||||
make it enforceable.
|
||||
.
|
||||
14. **Definition of "You" in This License.** "You" throughout this License,
|
||||
whether in upper or lower case, means an individual or a legal entity
|
||||
exercising rights under, and complying with all of the terms of, this License.
|
||||
For legal entities, "You" includes any entity that controls, is controlled by,
|
||||
or is under common control with you. For purposes of this definition, "control"
|
||||
means (i) the power, direct or indirect, to cause the direction or management
|
||||
of such entity, whether by contract or otherwise, or (ii) ownership of fifty
|
||||
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership
|
||||
of such entity.
|
||||
.
|
||||
15. **Right to Use.** You may use the Original Work in all ways not otherwise
|
||||
restricted or conditioned by this License or by law, and Licensor promises not
|
||||
to interfere with or be responsible for such uses by You.
|
||||
.
|
||||
16. **Modification of This License.** This License is Copyright © 2007 Zooko
|
||||
Wilcox-O'Hearn. Permission is granted to copy, distribute, or communicate this
|
||||
License without modification. Nothing in this License permits You to modify
|
||||
this License as applied to the Original Work or to Derivative Works. However,
|
||||
You may modify the text of this License and copy, distribute or communicate
|
||||
your modified version (the "Modified License") and apply it to other original
|
||||
works of authorship subject to the following conditions: (i) You may not
|
||||
indicate in any way that your Modified License is the "Bootstrap Open Source
|
||||
Licence" or "BOSL" and you may not use those names in the name of your Modified
|
||||
License; and (ii) You must replace the notice specified in the first paragraph
|
||||
above with the notice "Licensed under <insert your license name here>" or with
|
||||
a notice of your own that is not confusingly similar to the notice in this
|
||||
License.
|
||||
|
|
|
@ -42,7 +42,8 @@ JSON_TEST_FILES = \
|
|||
test/data/merkle_witness_serialization_sapling.json \
|
||||
test/data/merkle_path_sapling.json \
|
||||
test/data/merkle_commitments_sapling.json \
|
||||
test/data/sapling_key_components.json
|
||||
test/data/sapling_key_components.json \
|
||||
test/data/zip0244.json
|
||||
|
||||
RAW_TEST_FILES = test/data/alertTests.raw
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@ static const int32_t OVERWINTER_MAX_TX_VERSION = 3;
|
|||
static const int32_t SAPLING_MIN_TX_VERSION = 4;
|
||||
/** The maximum allowed Sapling transaction version (network rule) */
|
||||
static const int32_t SAPLING_MAX_TX_VERSION = 4;
|
||||
/** The minimum allowed ZIP225 transaction version (network rule) */
|
||||
static const int32_t ZIP225_MIN_TX_VERSION = 5;
|
||||
/** The maximum allowed ZIP225 transaction version (network rule) */
|
||||
static const int32_t ZIP225_MAX_TX_VERSION = 5;
|
||||
/** The maximum allowed size for a serialized block, in bytes (network rule) */
|
||||
static const unsigned int MAX_BLOCK_SIZE = 2000000;
|
||||
/** The maximum allowed number of signature check operations in a block (network rule) */
|
||||
|
|
|
@ -9,6 +9,66 @@
|
|||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
SaplingBundle::SaplingBundle(
|
||||
const std::vector<SpendDescription>& vShieldedSpend,
|
||||
const std::vector<OutputDescription>& vShieldedOutput,
|
||||
const CAmount& valueBalance,
|
||||
const binding_sig_t& bindingSig)
|
||||
: valueBalanceSapling(valueBalance), bindingSigSapling(bindingSig)
|
||||
{
|
||||
for (auto &spend : vShieldedSpend) {
|
||||
vSpendsSapling.emplace_back(spend.cv, spend.nullifier, spend.rk);
|
||||
if (anchorSapling.IsNull()) {
|
||||
anchorSapling = spend.anchor;
|
||||
} else {
|
||||
assert(anchorSapling == spend.anchor);
|
||||
}
|
||||
vSpendProofsSapling.push_back(spend.zkproof);
|
||||
vSpendAuthSigSapling.push_back(spend.spendAuthSig);
|
||||
}
|
||||
for (auto &output : vShieldedOutput) {
|
||||
vOutputsSapling.emplace_back(
|
||||
output.cv,
|
||||
output.cmu,
|
||||
output.ephemeralKey,
|
||||
output.encCiphertext,
|
||||
output.outCiphertext);
|
||||
vOutputProofsSapling.push_back(output.zkproof);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SpendDescription> SaplingBundle::GetV4ShieldedSpend()
|
||||
{
|
||||
std::vector<SpendDescription> vShieldedSpend;
|
||||
for (int i = 0; i < vSpendsSapling.size(); i++) {
|
||||
auto spend = vSpendsSapling[i];
|
||||
vShieldedSpend.emplace_back(
|
||||
spend.cv,
|
||||
anchorSapling,
|
||||
spend.nullifier,
|
||||
spend.rk,
|
||||
vSpendProofsSapling[i],
|
||||
vSpendAuthSigSapling[i]);
|
||||
}
|
||||
return vShieldedSpend;
|
||||
}
|
||||
|
||||
std::vector<OutputDescription> SaplingBundle::GetV4ShieldedOutput()
|
||||
{
|
||||
std::vector<OutputDescription> vShieldedOutput;
|
||||
for (int i = 0; i < vOutputsSapling.size(); i++) {
|
||||
auto output = vOutputsSapling[i];
|
||||
vShieldedOutput.emplace_back(
|
||||
output.cv,
|
||||
output.cmu,
|
||||
output.ephemeralKey,
|
||||
output.encCiphertext,
|
||||
output.outCiphertext,
|
||||
vOutputProofsSapling[i]);
|
||||
}
|
||||
return vShieldedOutput;
|
||||
}
|
||||
|
||||
std::string COutPoint::ToString() const
|
||||
{
|
||||
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
||||
|
@ -67,8 +127,10 @@ std::string CTxOut::ToString() const
|
|||
|
||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0), valueBalance(0) {}
|
||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
nConsensusBranchId(tx.GetConsensusBranchId()),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
orchardBundle(tx.GetOrchardBundle()),
|
||||
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
|
@ -86,14 +148,18 @@ void CTransaction::UpdateHash() const
|
|||
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION),
|
||||
fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0),
|
||||
nConsensusBranchId(0),
|
||||
vin(), vout(), nLockTime(0),
|
||||
valueBalance(0), vShieldedSpend(), vShieldedOutput(),
|
||||
orchardBundle(),
|
||||
vJoinSplit(), joinSplitPubKey(), joinSplitSig(),
|
||||
bindingSig() { }
|
||||
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
nConsensusBranchId(tx.nConsensusBranchId),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
orchardBundle(tx.orchardBundle),
|
||||
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
|
@ -105,8 +171,10 @@ CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion
|
|||
CTransaction::CTransaction(
|
||||
const CMutableTransaction &tx,
|
||||
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
|
||||
nConsensusBranchId(tx.nConsensusBranchId),
|
||||
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
|
||||
valueBalance(tx.valueBalance), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
|
||||
orchardBundle(tx.orchardBundle),
|
||||
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
|
||||
bindingSig(tx.bindingSig)
|
||||
{
|
||||
|
@ -115,10 +183,12 @@ CTransaction::CTransaction(
|
|||
|
||||
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion),
|
||||
fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId),
|
||||
nConsensusBranchId(tx.nConsensusBranchId),
|
||||
vin(std::move(tx.vin)), vout(std::move(tx.vout)),
|
||||
nLockTime(tx.nLockTime), nExpiryHeight(tx.nExpiryHeight),
|
||||
valueBalance(tx.valueBalance),
|
||||
vShieldedSpend(std::move(tx.vShieldedSpend)), vShieldedOutput(std::move(tx.vShieldedOutput)),
|
||||
orchardBundle(std::move(tx.orchardBundle)),
|
||||
vJoinSplit(std::move(tx.vJoinSplit)),
|
||||
joinSplitPubKey(std::move(tx.joinSplitPubKey)), joinSplitSig(std::move(tx.joinSplitSig)),
|
||||
bindingSig(std::move(tx.bindingSig))
|
||||
|
@ -130,6 +200,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<bool*>(&fOverwintered) = tx.fOverwintered;
|
||||
*const_cast<int*>(&nVersion) = tx.nVersion;
|
||||
*const_cast<uint32_t*>(&nVersionGroupId) = tx.nVersionGroupId;
|
||||
nConsensusBranchId = tx.nConsensusBranchId;
|
||||
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
|
||||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||
|
@ -137,6 +208,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<CAmount*>(&valueBalance) = tx.valueBalance;
|
||||
*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend) = tx.vShieldedSpend;
|
||||
*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput) = tx.vShieldedOutput;
|
||||
orchardBundle = tx.orchardBundle;
|
||||
*const_cast<std::vector<JSDescription>*>(&vJoinSplit) = tx.vJoinSplit;
|
||||
*const_cast<Ed25519VerificationKey*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||
*const_cast<Ed25519Signature*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "zcash/Proof.hpp"
|
||||
|
||||
#include <rust/ed25519/types.h>
|
||||
#include <rust/orchard.h>
|
||||
|
||||
// Overwinter transaction version group id
|
||||
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
|
||||
|
@ -46,6 +47,18 @@ static_assert(SAPLING_TX_VERSION >= SAPLING_MIN_TX_VERSION,
|
|||
static_assert(SAPLING_TX_VERSION <= SAPLING_MAX_TX_VERSION,
|
||||
"Sapling tx version must not be higher than maximum");
|
||||
|
||||
// ZIP225 transaction version group id
|
||||
// (defined in section 7.1 of the protocol spec)
|
||||
static constexpr uint32_t ZIP225_VERSION_GROUP_ID = 0x26A7270A;
|
||||
static_assert(ZIP225_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
||||
// ZIP225 transaction version
|
||||
static const int32_t ZIP225_TX_VERSION = 5;
|
||||
static_assert(ZIP225_TX_VERSION >= ZIP225_MIN_TX_VERSION,
|
||||
"ZIP225 tx version must not be lower than minimum");
|
||||
static_assert(ZIP225_TX_VERSION <= ZIP225_MAX_TX_VERSION,
|
||||
"ZIP225 tx version must not be higher than maximum");
|
||||
|
||||
// Future transaction version group id
|
||||
static constexpr uint32_t ZFUTURE_VERSION_GROUP_ID = 0xFFFFFFFF;
|
||||
static_assert(ZFUTURE_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
|
||||
|
@ -78,23 +91,54 @@ static inline size_t JOINSPLIT_SIZE(int transactionVersion) {
|
|||
return transactionVersion >= SAPLING_TX_VERSION ? 1698 : 1802;
|
||||
}
|
||||
|
||||
/**
|
||||
* The storage format for Sapling Spend descriptions in v5 transactions.
|
||||
*/
|
||||
class SpendDescriptionV5
|
||||
{
|
||||
public:
|
||||
uint256 cv; //!< A value commitment to the value of the input note.
|
||||
uint256 nullifier; //!< The nullifier of the input note.
|
||||
uint256 rk; //!< The randomized public key for spendAuthSig.
|
||||
|
||||
SpendDescriptionV5() { }
|
||||
|
||||
SpendDescriptionV5(uint256 cv, uint256 nullifier, uint256 rk)
|
||||
: cv(cv), nullifier(nullifier), rk(rk) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(cv);
|
||||
READWRITE(nullifier);
|
||||
READWRITE(rk);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A shielded input to a transaction. It contains data that describes a Spend transfer.
|
||||
*/
|
||||
class SpendDescription
|
||||
class SpendDescription : public SpendDescriptionV5
|
||||
{
|
||||
public:
|
||||
typedef std::array<unsigned char, 64> spend_auth_sig_t;
|
||||
|
||||
uint256 cv; //!< A value commitment to the value of the input note.
|
||||
uint256 anchor; //!< A Merkle root of the Sapling note commitment tree at some block height in the past.
|
||||
uint256 nullifier; //!< The nullifier of the input note.
|
||||
uint256 rk; //!< The randomized public key for spendAuthSig.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the spend circuit.
|
||||
spend_auth_sig_t spendAuthSig; //!< A signature authorizing this spend.
|
||||
|
||||
SpendDescription() { }
|
||||
|
||||
SpendDescription(
|
||||
uint256 cv,
|
||||
uint256 anchor,
|
||||
uint256 nullifier,
|
||||
uint256 rk,
|
||||
libzcash::GrothProof zkproof,
|
||||
spend_auth_sig_t spendAuthSig)
|
||||
: SpendDescriptionV5(cv, nullifier, rk), anchor(anchor), zkproof(zkproof), spendAuthSig(spendAuthSig) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
|
@ -126,9 +170,9 @@ public:
|
|||
};
|
||||
|
||||
/**
|
||||
* A shielded output to a transaction. It contains data that describes an Output transfer.
|
||||
* The storage format for Sapling Output descriptions in v5 transactions.
|
||||
*/
|
||||
class OutputDescription
|
||||
class OutputDescriptionV5
|
||||
{
|
||||
public:
|
||||
uint256 cv; //!< A value commitment to the value of the output note.
|
||||
|
@ -136,9 +180,16 @@ public:
|
|||
uint256 ephemeralKey; //!< A Jubjub public key.
|
||||
libzcash::SaplingEncCiphertext encCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::SaplingOutCiphertext outCiphertext; //!< A ciphertext component for the encrypted output note.
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the output circuit.
|
||||
|
||||
OutputDescription() { }
|
||||
OutputDescriptionV5() { }
|
||||
|
||||
OutputDescriptionV5(
|
||||
uint256 cv,
|
||||
uint256 cmu,
|
||||
uint256 ephemeralKey,
|
||||
libzcash::SaplingEncCiphertext encCiphertext,
|
||||
libzcash::SaplingOutCiphertext outCiphertext)
|
||||
: cv(cv), cmu(cmu), ephemeralKey(ephemeralKey), encCiphertext(encCiphertext), outCiphertext(outCiphertext) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
|
@ -149,6 +200,33 @@ public:
|
|||
READWRITE(ephemeralKey);
|
||||
READWRITE(encCiphertext);
|
||||
READWRITE(outCiphertext);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A shielded output to a transaction. It contains data that describes an Output transfer.
|
||||
*/
|
||||
class OutputDescription : public OutputDescriptionV5
|
||||
{
|
||||
public:
|
||||
libzcash::GrothProof zkproof; //!< A zero-knowledge proof using the output circuit.
|
||||
|
||||
OutputDescription() { }
|
||||
|
||||
OutputDescription(
|
||||
uint256 cv,
|
||||
uint256 cmu,
|
||||
uint256 ephemeralKey,
|
||||
libzcash::SaplingEncCiphertext encCiphertext,
|
||||
libzcash::SaplingOutCiphertext outCiphertext,
|
||||
libzcash::GrothProof zkproof)
|
||||
: OutputDescriptionV5(cv, cmu, ephemeralKey, encCiphertext, outCiphertext), zkproof(zkproof) { }
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
OutputDescriptionV5::SerializationOp(s, ser_action);
|
||||
READWRITE(zkproof);
|
||||
}
|
||||
|
||||
|
@ -170,6 +248,134 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The Sapling component of a v5 transaction.
|
||||
*/
|
||||
class SaplingBundle
|
||||
{
|
||||
private:
|
||||
typedef std::array<unsigned char, 64> binding_sig_t;
|
||||
|
||||
std::vector<SpendDescriptionV5> vSpendsSapling;
|
||||
std::vector<OutputDescriptionV5> vOutputsSapling;
|
||||
uint256 anchorSapling;
|
||||
std::vector<libzcash::GrothProof> vSpendProofsSapling;
|
||||
std::vector<SpendDescription::spend_auth_sig_t> vSpendAuthSigSapling;
|
||||
std::vector<libzcash::GrothProof> vOutputProofsSapling;
|
||||
|
||||
public:
|
||||
CAmount valueBalanceSapling;
|
||||
binding_sig_t bindingSigSapling = {{0}};
|
||||
|
||||
SaplingBundle() : valueBalanceSapling(0) {}
|
||||
|
||||
SaplingBundle(
|
||||
const std::vector<SpendDescription>& vShieldedSpend,
|
||||
const std::vector<OutputDescription>& vShieldedOutput,
|
||||
const CAmount& valueBalance,
|
||||
const binding_sig_t& bindingSig);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(vSpendsSapling);
|
||||
READWRITE(vOutputsSapling);
|
||||
|
||||
bool hasSapling = !(vSpendsSapling.empty() && vOutputsSapling.empty());
|
||||
|
||||
if (hasSapling) {
|
||||
READWRITE(valueBalanceSapling);
|
||||
}
|
||||
if (!vSpendsSapling.empty()) {
|
||||
READWRITE(anchorSapling);
|
||||
}
|
||||
if (ser_action.ForRead()) {
|
||||
for (auto &spend : vSpendsSapling) {
|
||||
libzcash::GrothProof zkproof;
|
||||
READWRITE(zkproof);
|
||||
vSpendProofsSapling.push_back(zkproof);
|
||||
}
|
||||
for (auto &spend : vSpendsSapling) {
|
||||
SpendDescription::spend_auth_sig_t spendAuthSig;
|
||||
READWRITE(spendAuthSig);
|
||||
vSpendAuthSigSapling.push_back(spendAuthSig);
|
||||
}
|
||||
for (auto &output : vOutputsSapling) {
|
||||
libzcash::GrothProof zkproof;
|
||||
READWRITE(zkproof);
|
||||
vOutputProofsSapling.push_back(zkproof);
|
||||
}
|
||||
} else {
|
||||
for (auto &zkproof : vSpendProofsSapling) {
|
||||
READWRITE(zkproof);
|
||||
}
|
||||
for (auto &spendAuthSig : vSpendAuthSigSapling) {
|
||||
READWRITE(spendAuthSig);
|
||||
}
|
||||
for (auto &zkproof : vOutputProofsSapling) {
|
||||
READWRITE(zkproof);
|
||||
}
|
||||
}
|
||||
if (hasSapling) {
|
||||
READWRITE(bindingSigSapling);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SpendDescription> GetV4ShieldedSpend();
|
||||
std::vector<OutputDescription> GetV4ShieldedOutput();
|
||||
};
|
||||
|
||||
/**
|
||||
* The Orchard component of a transaction.
|
||||
*/
|
||||
class OrchardBundle
|
||||
{
|
||||
private:
|
||||
/// An optional Orchard bundle (with `nullptr` corresponding to `None`).
|
||||
/// Memory is allocated by Rust.
|
||||
std::unique_ptr<OrchardBundlePtr, decltype(&orchard_bundle_free)> inner;
|
||||
|
||||
public:
|
||||
OrchardBundle() : inner(nullptr, orchard_bundle_free) {}
|
||||
|
||||
OrchardBundle(OrchardBundle&& bundle) : inner(std::move(bundle.inner)) {}
|
||||
OrchardBundle(const OrchardBundle& bundle) :
|
||||
inner(orchard_bundle_clone(bundle.inner.get()), orchard_bundle_free) {}
|
||||
OrchardBundle& operator=(OrchardBundle&& bundle)
|
||||
{
|
||||
if (this != &bundle) {
|
||||
inner = std::move(bundle.inner);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
OrchardBundle& operator=(const OrchardBundle& bundle)
|
||||
{
|
||||
if (this != &bundle) {
|
||||
inner.reset(orchard_bundle_clone(bundle.inner.get()));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const {
|
||||
RustStream rs(s);
|
||||
if (!orchard_bundle_serialize(inner.get(), &rs, RustStream<Stream>::write_callback)) {
|
||||
throw std::ios_base::failure("Failed to serialize v5 Orchard bundle");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s) {
|
||||
RustStream rs(s);
|
||||
OrchardBundlePtr* bundle;
|
||||
if (!orchard_bundle_parse(&rs, RustStream<Stream>::read_callback, &bundle)) {
|
||||
throw std::ios_base::failure("Failed to parse v5 Orchard bundle");
|
||||
}
|
||||
inner.reset(bundle);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Stream>
|
||||
class SproutProofSerializer
|
||||
{
|
||||
|
@ -501,6 +707,11 @@ struct CMutableTransaction;
|
|||
class CTransaction
|
||||
{
|
||||
private:
|
||||
/// The consensus branch ID that this transaction commits to.
|
||||
/// Serialized from v5 onwards.
|
||||
uint32_t nConsensusBranchId;
|
||||
OrchardBundle orchardBundle;
|
||||
|
||||
/** Memory only. */
|
||||
const uint256 hash;
|
||||
void UpdateHash() const;
|
||||
|
@ -597,6 +808,11 @@ public:
|
|||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
|
||||
bool isZip225V5 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == ZIP225_VERSION_GROUP_ID &&
|
||||
nVersion == ZIP225_TX_VERSION;
|
||||
|
||||
// It is not possible to make the transaction's serialized form vary on
|
||||
// a per-enabled-feature basis. The approach here is that all
|
||||
// serialization rules for not-yet-released features must be
|
||||
|
@ -607,32 +823,66 @@ public:
|
|||
nVersionGroupId == ZFUTURE_VERSION_GROUP_ID &&
|
||||
nVersion == ZFUTURE_TX_VERSION;
|
||||
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4 || isFuture)) {
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4 || isZip225V5 || isFuture)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (isOverwinterV3 || isSaplingV4 || isFuture) {
|
||||
if (isZip225V5) {
|
||||
// Common Transaction Fields (plus version bytes above)
|
||||
READWRITE(nConsensusBranchId);
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
|
||||
}
|
||||
if (isSaplingV4 || isFuture) {
|
||||
READWRITE(*const_cast<CAmount*>(&valueBalance));
|
||||
READWRITE(*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend));
|
||||
READWRITE(*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput));
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
// These fields do not depend on fOverwintered
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, *const_cast<std::vector<JSDescription>*>(&vJoinSplit), ser_action);
|
||||
if (vJoinSplit.size() > 0) {
|
||||
READWRITE(*const_cast<Ed25519VerificationKey*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<Ed25519Signature*>(&joinSplitSig));
|
||||
|
||||
// Transparent Transaction Fields
|
||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
|
||||
// Sapling Transaction Fields
|
||||
if (ser_action.ForRead()) {
|
||||
SaplingBundle saplingBundle;
|
||||
READWRITE(saplingBundle);
|
||||
*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend) =
|
||||
saplingBundle.GetV4ShieldedSpend();
|
||||
*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput) =
|
||||
saplingBundle.GetV4ShieldedOutput();
|
||||
*const_cast<CAmount*>(&valueBalance) = saplingBundle.valueBalanceSapling;
|
||||
*const_cast<binding_sig_t*>(&bindingSig) = saplingBundle.bindingSigSapling;
|
||||
} else {
|
||||
SaplingBundle saplingBundle(
|
||||
vShieldedSpend,
|
||||
vShieldedOutput,
|
||||
valueBalance,
|
||||
bindingSig);
|
||||
READWRITE(saplingBundle);
|
||||
}
|
||||
|
||||
// Orchard Transaction Fields
|
||||
READWRITE(orchardBundle);
|
||||
} else {
|
||||
// Legacy transaction formats
|
||||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (isOverwinterV3 || isSaplingV4 || isFuture) {
|
||||
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
|
||||
}
|
||||
if (isSaplingV4 || isFuture) {
|
||||
READWRITE(*const_cast<CAmount*>(&valueBalance));
|
||||
READWRITE(*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend));
|
||||
READWRITE(*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput));
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
// These fields do not depend on fOverwintered
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, *const_cast<std::vector<JSDescription>*>(&vJoinSplit), ser_action);
|
||||
if (vJoinSplit.size() > 0) {
|
||||
READWRITE(*const_cast<Ed25519VerificationKey*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<Ed25519Signature*>(&joinSplitSig));
|
||||
}
|
||||
}
|
||||
if ((isSaplingV4 || isFuture) && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(*const_cast<binding_sig_t*>(&bindingSig));
|
||||
}
|
||||
}
|
||||
if ((isSaplingV4 || isFuture) && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(*const_cast<binding_sig_t*>(&bindingSig));
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
UpdateHash();
|
||||
|
@ -659,6 +909,14 @@ public:
|
|||
return header;
|
||||
}
|
||||
|
||||
uint32_t GetConsensusBranchId() const {
|
||||
return nConsensusBranchId;
|
||||
}
|
||||
|
||||
const OrchardBundle& GetOrchardBundle() const {
|
||||
return orchardBundle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context for the two methods below:
|
||||
* As at most one of vpub_new and vpub_old is non-zero in every JoinSplit,
|
||||
|
@ -708,6 +966,9 @@ struct CMutableTransaction
|
|||
bool fOverwintered;
|
||||
int32_t nVersion;
|
||||
uint32_t nVersionGroupId;
|
||||
/// The consensus branch ID that this transaction commits to.
|
||||
/// Serialized from v5 onwards.
|
||||
uint32_t nConsensusBranchId;
|
||||
std::vector<CTxIn> vin;
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
|
@ -715,6 +976,7 @@ struct CMutableTransaction
|
|||
CAmount valueBalance;
|
||||
std::vector<SpendDescription> vShieldedSpend;
|
||||
std::vector<OutputDescription> vShieldedOutput;
|
||||
OrchardBundle orchardBundle;
|
||||
std::vector<JSDescription> vJoinSplit;
|
||||
Ed25519VerificationKey joinSplitPubKey;
|
||||
Ed25519Signature joinSplitSig;
|
||||
|
@ -754,35 +1016,71 @@ struct CMutableTransaction
|
|||
fOverwintered &&
|
||||
nVersionGroupId == SAPLING_VERSION_GROUP_ID &&
|
||||
nVersion == SAPLING_TX_VERSION;
|
||||
bool isZip225V5 =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == ZIP225_VERSION_GROUP_ID &&
|
||||
nVersion == ZIP225_TX_VERSION;
|
||||
bool isFuture =
|
||||
fOverwintered &&
|
||||
nVersionGroupId == ZFUTURE_VERSION_GROUP_ID &&
|
||||
nVersion == ZFUTURE_TX_VERSION;
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4 || isFuture)) {
|
||||
if (fOverwintered && !(isOverwinterV3 || isSaplingV4 || isZip225V5 || isFuture)) {
|
||||
throw std::ios_base::failure("Unknown transaction format");
|
||||
}
|
||||
|
||||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (isOverwinterV3 || isSaplingV4 || isFuture) {
|
||||
if (isZip225V5) {
|
||||
// Common Transaction Fields (plus version bytes above)
|
||||
READWRITE(nConsensusBranchId);
|
||||
READWRITE(nLockTime);
|
||||
READWRITE(nExpiryHeight);
|
||||
}
|
||||
if (isSaplingV4 || isFuture) {
|
||||
READWRITE(valueBalance);
|
||||
READWRITE(vShieldedSpend);
|
||||
READWRITE(vShieldedOutput);
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, vJoinSplit, ser_action);
|
||||
if (vJoinSplit.size() > 0) {
|
||||
READWRITE(joinSplitPubKey);
|
||||
READWRITE(joinSplitSig);
|
||||
|
||||
// Transparent Transaction Fields
|
||||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
|
||||
// Sapling Transaction Fields
|
||||
if (ser_action.ForRead()) {
|
||||
SaplingBundle saplingBundle;
|
||||
READWRITE(saplingBundle);
|
||||
vShieldedSpend = saplingBundle.GetV4ShieldedSpend();
|
||||
vShieldedOutput = saplingBundle.GetV4ShieldedOutput();
|
||||
valueBalance = saplingBundle.valueBalanceSapling;
|
||||
bindingSig = saplingBundle.bindingSigSapling;
|
||||
} else {
|
||||
SaplingBundle saplingBundle(
|
||||
vShieldedSpend,
|
||||
vShieldedOutput,
|
||||
valueBalance,
|
||||
bindingSig);
|
||||
READWRITE(saplingBundle);
|
||||
}
|
||||
|
||||
// Orchard Transaction Fields
|
||||
READWRITE(orchardBundle);
|
||||
} else {
|
||||
// Legacy transaction formats
|
||||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (isOverwinterV3 || isSaplingV4 || isFuture) {
|
||||
READWRITE(nExpiryHeight);
|
||||
}
|
||||
if (isSaplingV4 || isFuture) {
|
||||
READWRITE(valueBalance);
|
||||
READWRITE(vShieldedSpend);
|
||||
READWRITE(vShieldedOutput);
|
||||
}
|
||||
if (nVersion >= 2) {
|
||||
auto os = WithVersion(&s, static_cast<int>(header));
|
||||
::SerReadWrite(os, vJoinSplit, ser_action);
|
||||
if (vJoinSplit.size() > 0) {
|
||||
READWRITE(joinSplitPubKey);
|
||||
READWRITE(joinSplitSig);
|
||||
}
|
||||
}
|
||||
if ((isSaplingV4 || isFuture) && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(bindingSig);
|
||||
}
|
||||
}
|
||||
if ((isSaplingV4 || isFuture) && !(vShieldedSpend.empty() && vShieldedOutput.empty())) {
|
||||
READWRITE(bindingSig);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// 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_ORCHARD_H
|
||||
#define ZCASH_RUST_INCLUDE_RUST_ORCHARD_H
|
||||
|
||||
#include "rust/streams.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct OrchardBundlePtr;
|
||||
typedef struct OrchardBundlePtr OrchardBundlePtr;
|
||||
|
||||
/// Clones the given Orchard bundle.
|
||||
///
|
||||
/// Both bundles need to be separately freed when they go out of scope.
|
||||
OrchardBundlePtr* orchard_bundle_clone(const OrchardBundlePtr* bundle);
|
||||
|
||||
/// Frees an Orchard bundle returned from `orchard_parse_bundle`.
|
||||
void orchard_bundle_free(OrchardBundlePtr* bundle);
|
||||
|
||||
/// Parses an authorized Orchard bundle from the given stream.
|
||||
///
|
||||
/// - If no error occurs, `bundle_ret` will point to a Rust-allocated Orchard bundle.
|
||||
/// - If an error occurs, `bundle_ret` will be unaltered.
|
||||
bool orchard_bundle_parse(
|
||||
void* stream,
|
||||
read_callback_t read_cb,
|
||||
OrchardBundlePtr** bundle_ret);
|
||||
|
||||
/// Serializes an authorized Orchard bundle to the given stream
|
||||
///
|
||||
/// If `bundle == nullptr`, this serializes `nActionsOrchard = 0`.
|
||||
bool orchard_bundle_serialize(
|
||||
const OrchardBundlePtr* bundle,
|
||||
void* stream,
|
||||
write_callback_t write_cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ZCASH_RUST_INCLUDE_RUST_ORCHARD_H
|
|
@ -0,0 +1,23 @@
|
|||
// 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_STREAMS_H
|
||||
#define ZCASH_RUST_INCLUDE_RUST_STREAMS_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// The type that Rust expects for its `CppStreamReader` callback.
|
||||
typedef long (*read_callback_t)(void* context, unsigned char* pch, size_t nSize);
|
||||
/// The type that Rust expects for its `CppStreamWriter` callback.
|
||||
typedef long (*write_callback_t)(void* context, const unsigned char* pch, size_t nSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ZCASH_RUST_INCLUDE_RUST_STREAMS_H
|
|
@ -0,0 +1,67 @@
|
|||
use std::ptr;
|
||||
|
||||
use orchard::{bundle::Authorized, Bundle};
|
||||
use tracing::error;
|
||||
use zcash_primitives::transaction::components::{orchard as orchard_serialization, Amount};
|
||||
|
||||
use crate::streams_ffi::{CppStreamReader, CppStreamWriter, ReadCb, StreamObj, WriteCb};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_bundle_clone(
|
||||
bundle: *const Bundle<Authorized, Amount>,
|
||||
) -> *mut Bundle<Authorized, Amount> {
|
||||
unsafe { bundle.as_ref() }
|
||||
.map(|bundle| Box::into_raw(Box::new(bundle.clone())))
|
||||
.unwrap_or(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_bundle_free(bundle: *mut Bundle<Authorized, Amount>) {
|
||||
if !bundle.is_null() {
|
||||
drop(unsafe { Box::from_raw(bundle) });
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_bundle_parse(
|
||||
stream: Option<StreamObj>,
|
||||
read_cb: Option<ReadCb>,
|
||||
bundle_ret: *mut *mut Bundle<Authorized, Amount>,
|
||||
) -> bool {
|
||||
let reader = CppStreamReader::from_raw_parts(stream, read_cb.unwrap());
|
||||
|
||||
match orchard_serialization::read_v5_bundle(reader) {
|
||||
Ok(parsed) => {
|
||||
unsafe {
|
||||
*bundle_ret = if let Some(bundle) = parsed {
|
||||
Box::into_raw(Box::new(bundle))
|
||||
} else {
|
||||
ptr::null_mut::<Bundle<Authorized, Amount>>()
|
||||
};
|
||||
};
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to parse Orchard bundle: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn orchard_bundle_serialize(
|
||||
bundle: *const Bundle<Authorized, Amount>,
|
||||
stream: Option<StreamObj>,
|
||||
write_cb: Option<WriteCb>,
|
||||
) -> bool {
|
||||
let bundle = unsafe { bundle.as_ref() };
|
||||
let writer = CppStreamWriter::from_raw_parts(stream, write_cb.unwrap());
|
||||
|
||||
match orchard_serialization::write_v5_bundle(bundle, writer) {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,8 +66,11 @@ use zcash_history::{Entry as MMREntry, NodeData as MMRNodeData, Tree as MMRTree}
|
|||
mod blake2b;
|
||||
mod ed25519;
|
||||
mod metrics_ffi;
|
||||
mod streams_ffi;
|
||||
mod tracing_ffi;
|
||||
|
||||
mod orchard_ffi;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
use std::io;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use libc::{c_long, c_void, size_t};
|
||||
|
||||
pub type StreamObj = NonNull<c_void>;
|
||||
pub type ReadCb =
|
||||
unsafe extern "C" fn(obj: Option<StreamObj>, pch: *mut u8, size: size_t) -> c_long;
|
||||
pub type WriteCb =
|
||||
unsafe extern "C" fn(obj: Option<StreamObj>, pch: *const u8, size: size_t) -> c_long;
|
||||
|
||||
pub struct CppStreamReader {
|
||||
inner: Option<StreamObj>,
|
||||
cb: ReadCb,
|
||||
}
|
||||
|
||||
impl CppStreamReader {
|
||||
pub fn from_raw_parts(inner: Option<StreamObj>, cb: ReadCb) -> Self {
|
||||
CppStreamReader { inner, cb }
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for CppStreamReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match unsafe { (self.cb)(self.inner, buf.as_mut_ptr(), buf.len()) } {
|
||||
-1 => Err(io::Error::new(io::ErrorKind::Other, "C++ stream error")),
|
||||
n => Ok(n as usize),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CppStreamWriter {
|
||||
inner: Option<StreamObj>,
|
||||
cb: WriteCb,
|
||||
}
|
||||
|
||||
impl CppStreamWriter {
|
||||
pub fn from_raw_parts(inner: Option<StreamObj>, cb: WriteCb) -> Self {
|
||||
CppStreamWriter { inner, cb }
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for CppStreamWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match unsafe { (self.cb)(self.inner, buf.as_ptr(), buf.len()) } {
|
||||
-1 => Err(io::Error::new(io::ErrorKind::Other, "C++ stream error")),
|
||||
n => Ok(n as usize),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -22,6 +22,51 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Wrapper around C++ stream objects, enabling them to be passed into Rust code.
|
||||
*/
|
||||
template<typename Stream>
|
||||
class RustStream {
|
||||
Stream* stream;
|
||||
|
||||
public:
|
||||
RustStream(Stream& stream_) : stream(&stream_) {}
|
||||
|
||||
static long read_callback(void* context, unsigned char* pch, size_t nSize)
|
||||
{
|
||||
return reinterpret_cast<RustStream*>(context)->read(
|
||||
reinterpret_cast<char*>(pch), nSize);
|
||||
}
|
||||
|
||||
static long write_callback(void* context, const unsigned char* pch, size_t nSize)
|
||||
{
|
||||
return reinterpret_cast<RustStream*>(context)->write(
|
||||
reinterpret_cast<const char*>(pch), nSize);
|
||||
}
|
||||
|
||||
long read(char* pch, size_t nSize)
|
||||
{
|
||||
try {
|
||||
stream->read(pch, nSize);
|
||||
return nSize;
|
||||
} catch (std::ios_base::failure e) {
|
||||
// TODO: log
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long write(const char* pch, size_t nSize)
|
||||
{
|
||||
try {
|
||||
stream->write(pch, nSize);
|
||||
return nSize;
|
||||
} catch (std::ios_base::failure e) {
|
||||
// TODO: log
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Stream>
|
||||
class OverrideStream
|
||||
{
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "test/data/tx_invalid.json.h"
|
||||
#include "test/data/tx_valid.json.h"
|
||||
#include "test/data/zip0244.json.h"
|
||||
#include "test/test_bitcoin.h"
|
||||
|
||||
#include "init.h"
|
||||
|
@ -834,4 +835,40 @@ BOOST_AUTO_TEST_CASE(test_IsStandardV2)
|
|||
BOOST_CHECK(!IsStandardTx(t, reason, chainparams));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TxV5)
|
||||
{
|
||||
// [
|
||||
// tx,
|
||||
// txid,
|
||||
// auth_digest,
|
||||
// Option<transparent_input>,
|
||||
// Option<script_code>,
|
||||
// Option<amount>,
|
||||
// sighash_all,
|
||||
// Option<sighash_none>,
|
||||
// Option<sighash_single>,
|
||||
// Option<sighash_all_anyone>,
|
||||
// Option<sighash_none_anyone>,
|
||||
// Option<sighash_single_anyone>,
|
||||
// ]
|
||||
//
|
||||
// The optional values are all set together.
|
||||
UniValue tests = read_json(std::string(json_tests::zip0244, json_tests::zip0244 + sizeof(json_tests::zip0244)));
|
||||
|
||||
// Skipping over comments in zip0244.json file
|
||||
for (size_t idx = 2; idx < tests.size(); idx++) {
|
||||
UniValue test = tests[idx];
|
||||
|
||||
std::string transaction = test[0].get_str();
|
||||
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
|
||||
CTransaction tx;
|
||||
stream >> tx;
|
||||
|
||||
// Check that re-serializing the transaction gives the same encoding.
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << tx;
|
||||
BOOST_CHECK_EQUAL(HexStr(ss.begin(), ss.end()), transaction);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
Loading…
Reference in New Issue