Jack Grigg
6 years ago
6 changed files with 327 additions and 2 deletions
@ -0,0 +1,80 @@ |
|||
#include "chainparams.h" |
|||
#include "consensus/params.h" |
|||
#include "consensus/validation.h" |
|||
#include "main.h" |
|||
#include "transaction_builder.h" |
|||
#include "zcash/Address.hpp" |
|||
|
|||
#include <gtest/gtest.h> |
|||
#include <gmock/gmock.h> |
|||
|
|||
TEST(TransactionBuilder, Invoke) { |
|||
SelectParams(CBaseChainParams::REGTEST); |
|||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); |
|||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE); |
|||
auto consensusParams = Params().GetConsensus(); |
|||
|
|||
auto sk_from = libzcash::SaplingSpendingKey::random(); |
|||
auto fvk_from = sk_from.full_viewing_key(); |
|||
|
|||
auto sk = libzcash::SaplingSpendingKey::random(); |
|||
auto xsk = sk.expanded_spending_key(); |
|||
auto fvk = sk.full_viewing_key(); |
|||
auto ivk = fvk.in_viewing_key(); |
|||
libzcash::diversifier_t d = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
|||
auto pk = *ivk.address(d); |
|||
|
|||
// Create a shielding transaction from transparent to Sapling
|
|||
// TODO: Add transparent inputs :P
|
|||
auto builder1 = TransactionBuilder(consensusParams, 1); |
|||
builder1.AddSaplingOutput(fvk_from, pk, 50, {}); |
|||
auto maybe_tx1 = builder1.Build(); |
|||
ASSERT_EQ(static_cast<bool>(maybe_tx1), true); |
|||
auto tx1 = maybe_tx1.get(); |
|||
|
|||
EXPECT_EQ(tx1.vin.size(), 0); |
|||
EXPECT_EQ(tx1.vout.size(), 0); |
|||
EXPECT_EQ(tx1.vjoinsplit.size(), 0); |
|||
EXPECT_EQ(tx1.vShieldedSpend.size(), 0); |
|||
EXPECT_EQ(tx1.vShieldedOutput.size(), 1); |
|||
EXPECT_EQ(tx1.valueBalance, -50); |
|||
|
|||
CValidationState state; |
|||
EXPECT_TRUE(ContextualCheckTransaction(tx1, state, 2, 0)); |
|||
EXPECT_EQ(state.GetRejectReason(), ""); |
|||
|
|||
// Prepare to spend the note that was just created
|
|||
auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt( |
|||
tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey |
|||
); |
|||
ASSERT_EQ(static_cast<bool>(maybe_pt), true); |
|||
auto maybe_note = maybe_pt.get().note(ivk); |
|||
ASSERT_EQ(static_cast<bool>(maybe_note), true); |
|||
auto note = maybe_note.get(); |
|||
ZCSaplingIncrementalMerkleTree tree; |
|||
tree.append(tx1.vShieldedOutput[0].cm); |
|||
auto anchor = tree.root(); |
|||
auto witness = tree.witness(); |
|||
|
|||
// Create a Sapling-only transaction
|
|||
auto builder2 = TransactionBuilder(consensusParams, 2); |
|||
builder2.AddSaplingSpend(xsk, note, anchor, witness); |
|||
builder2.AddSaplingOutput(fvk, pk, 25, {}); |
|||
auto maybe_tx2 = builder2.Build(); |
|||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true); |
|||
auto tx2 = maybe_tx2.get(); |
|||
|
|||
EXPECT_EQ(tx2.vin.size(), 0); |
|||
EXPECT_EQ(tx2.vout.size(), 0); |
|||
EXPECT_EQ(tx2.vjoinsplit.size(), 0); |
|||
EXPECT_EQ(tx2.vShieldedSpend.size(), 1); |
|||
EXPECT_EQ(tx2.vShieldedOutput.size(), 1); |
|||
EXPECT_EQ(tx2.valueBalance, 25); |
|||
|
|||
EXPECT_TRUE(ContextualCheckTransaction(tx2, state, 3, 0)); |
|||
EXPECT_EQ(state.GetRejectReason(), ""); |
|||
|
|||
// Revert to default
|
|||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); |
|||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); |
|||
} |
@ -0,0 +1,169 @@ |
|||
// Copyright (c) 2018 The Zcash developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#include "transaction_builder.h" |
|||
|
|||
#include "main.h" |
|||
#include "script/script.h" |
|||
|
|||
#include <boost/variant.hpp> |
|||
#include <librustzcash.h> |
|||
|
|||
SpendDescriptionInfo::SpendDescriptionInfo( |
|||
libzcash::SaplingExpandedSpendingKey xsk, |
|||
libzcash::SaplingNote note, |
|||
uint256 anchor, |
|||
ZCSaplingIncrementalWitness witness |
|||
) : xsk(xsk), note(note), anchor(anchor), witness(witness) |
|||
{ |
|||
librustzcash_sapling_generate_r(alpha.begin()); |
|||
} |
|||
|
|||
TransactionBuilder::TransactionBuilder( |
|||
const Consensus::Params& consensusParams, int nHeight |
|||
) : consensusParams(consensusParams), nHeight(nHeight) |
|||
{ |
|||
mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight); |
|||
} |
|||
|
|||
void TransactionBuilder::AddSaplingSpend( |
|||
libzcash::SaplingExpandedSpendingKey xsk, |
|||
libzcash::SaplingNote note, |
|||
uint256 anchor, |
|||
ZCSaplingIncrementalWitness witness |
|||
) { |
|||
spends.emplace_back(xsk, note, anchor, witness); |
|||
mtx.valueBalance += note.value(); |
|||
} |
|||
|
|||
void TransactionBuilder::AddSaplingOutput( |
|||
libzcash::SaplingFullViewingKey from, |
|||
libzcash::SaplingPaymentAddress to, |
|||
CAmount value, |
|||
std::array<unsigned char, ZC_MEMO_SIZE> memo |
|||
) { |
|||
auto note = libzcash::SaplingNote(to, value); |
|||
outputs.emplace_back(from.ovk, note, memo); |
|||
mtx.valueBalance -= value; |
|||
} |
|||
|
|||
boost::optional<CTransaction> TransactionBuilder::Build() |
|||
{ |
|||
auto ctx = librustzcash_sapling_proving_ctx_init(); |
|||
|
|||
// Create Sapling SpendDescriptions
|
|||
for (auto spend : spends) { |
|||
auto cm = spend.note.cm(); |
|||
auto nf = spend.note.nullifier( |
|||
spend.xsk.full_viewing_key(), spend.witness.position()); |
|||
if (!(cm && nf)) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
|||
ss << spend.witness.path(); |
|||
std::vector<unsigned char> witness(ss.begin(), ss.end()); |
|||
|
|||
SpendDescription sdesc; |
|||
if (!librustzcash_sapling_spend_proof( |
|||
ctx, |
|||
spend.xsk.full_viewing_key().ak.begin(), |
|||
spend.xsk.nsk.begin(), |
|||
spend.note.d.data(), |
|||
spend.note.r.begin(), |
|||
spend.alpha.begin(), |
|||
spend.note.value(), |
|||
spend.anchor.begin(), |
|||
witness.data(), |
|||
sdesc.cv.begin(), |
|||
sdesc.rk.begin(), |
|||
sdesc.zkproof.data() |
|||
)) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
|
|||
sdesc.anchor = spend.anchor; |
|||
sdesc.nullifier = *nf; |
|||
mtx.vShieldedSpend.push_back(sdesc); |
|||
} |
|||
|
|||
// Create Sapling OutputDescriptions
|
|||
for (auto output : outputs) { |
|||
auto cm = output.note.cm(); |
|||
if (!cm) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
|
|||
libzcash::SaplingNotePlaintext notePlaintext(output.note, output.memo); |
|||
|
|||
auto res = notePlaintext.encrypt(output.note.pk_d); |
|||
if (!res) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
auto enc = res.get(); |
|||
auto encryptor = enc.second; |
|||
|
|||
OutputDescription odesc; |
|||
if (!librustzcash_sapling_output_proof( |
|||
ctx, |
|||
encryptor.get_esk().begin(), |
|||
output.note.d.data(), |
|||
output.note.pk_d.begin(), |
|||
output.note.r.begin(), |
|||
output.note.value(), |
|||
odesc.cv.begin(), |
|||
odesc.zkproof.begin() |
|||
)) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
|
|||
odesc.cm = *cm; |
|||
odesc.ephemeralKey = encryptor.get_epk(); |
|||
odesc.encCiphertext = enc.first; |
|||
|
|||
libzcash::SaplingOutgoingPlaintext outPlaintext(output.note.pk_d, encryptor.get_esk()); |
|||
odesc.outCiphertext = outPlaintext.encrypt( |
|||
output.ovk, |
|||
odesc.cv, |
|||
odesc.cm, |
|||
encryptor |
|||
); |
|||
mtx.vShieldedOutput.push_back(odesc); |
|||
} |
|||
|
|||
// Calculate SignatureHash
|
|||
auto consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams); |
|||
|
|||
// Empty output script.
|
|||
uint256 dataToBeSigned; |
|||
CScript scriptCode; |
|||
try { |
|||
dataToBeSigned = SignatureHash(scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); |
|||
} catch (std::logic_error ex) { |
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return boost::none; |
|||
} |
|||
|
|||
// Create Sapling spendAuth and binding signatures
|
|||
for (size_t i = 0; i < spends.size(); i++) { |
|||
librustzcash_sapling_spend_sig( |
|||
spends[i].xsk.ask.begin(), |
|||
spends[i].alpha.begin(), |
|||
dataToBeSigned.begin(), |
|||
mtx.vShieldedSpend[i].spendAuthSig.data()); |
|||
} |
|||
librustzcash_sapling_binding_sig( |
|||
ctx, |
|||
mtx.valueBalance, |
|||
dataToBeSigned.begin(), |
|||
mtx.bindingSig.data()); |
|||
|
|||
librustzcash_sapling_proving_ctx_free(ctx); |
|||
return CTransaction(mtx); |
|||
} |
@ -0,0 +1,73 @@ |
|||
// Copyright (c) 2018 The Zcash developers
|
|||
// Distributed under the MIT software license, see the accompanying
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|||
|
|||
#ifndef TRANSACTION_BUILDER_H |
|||
#define TRANSACTION_BUILDER_H |
|||
|
|||
#include "consensus/params.h" |
|||
#include "primitives/transaction.h" |
|||
#include "uint256.h" |
|||
#include "zcash/Address.hpp" |
|||
#include "zcash/IncrementalMerkleTree.hpp" |
|||
#include "zcash/Note.hpp" |
|||
#include "zcash/NoteEncryption.hpp" |
|||
|
|||
#include <boost/optional.hpp> |
|||
|
|||
struct SpendDescriptionInfo |
|||
{ |
|||
libzcash::SaplingExpandedSpendingKey xsk; |
|||
libzcash::SaplingNote note; |
|||
uint256 alpha; |
|||
uint256 anchor; |
|||
ZCSaplingIncrementalWitness witness; |
|||
|
|||
SpendDescriptionInfo( |
|||
libzcash::SaplingExpandedSpendingKey xsk, |
|||
libzcash::SaplingNote note, |
|||
uint256 anchor, |
|||
ZCSaplingIncrementalWitness witness); |
|||
}; |
|||
|
|||
struct OutputDescriptionInfo |
|||
{ |
|||
uint256 ovk; |
|||
libzcash::SaplingNote note; |
|||
std::array<unsigned char, ZC_MEMO_SIZE> memo; |
|||
|
|||
OutputDescriptionInfo( |
|||
uint256 ovk, |
|||
libzcash::SaplingNote note, |
|||
std::array<unsigned char, ZC_MEMO_SIZE> memo) : ovk(ovk), note(note), memo(memo) {} |
|||
}; |
|||
|
|||
class TransactionBuilder |
|||
{ |
|||
private: |
|||
Consensus::Params consensusParams; |
|||
int nHeight; |
|||
CMutableTransaction mtx; |
|||
|
|||
std::vector<SpendDescriptionInfo> spends; |
|||
std::vector<OutputDescriptionInfo> outputs; |
|||
|
|||
public: |
|||
TransactionBuilder(const Consensus::Params& consensusParams, int nHeight); |
|||
|
|||
void AddSaplingSpend( |
|||
libzcash::SaplingExpandedSpendingKey xsk, |
|||
libzcash::SaplingNote note, |
|||
uint256 anchor, |
|||
ZCSaplingIncrementalWitness witness); |
|||
|
|||
void AddSaplingOutput( |
|||
libzcash::SaplingFullViewingKey from, |
|||
libzcash::SaplingPaymentAddress to, |
|||
CAmount value, |
|||
std::array<unsigned char, ZC_MEMO_SIZE> memo); |
|||
|
|||
boost::optional<CTransaction> Build(); |
|||
}; |
|||
|
|||
#endif /* TRANSACTION_BUILDER_H */ |
Loading…
Reference in new issue