Browse Source

TransactionBuilder with support for creating Sapling-only transactions

pull/4/head
Jack Grigg 6 years ago
parent
commit
e691e21f40
No known key found for this signature in database GPG Key ID: 1B8D649257DB0829
  1. 4
      depends/packages/librustzcash.mk
  2. 2
      src/Makefile.am
  3. 1
      src/Makefile.gtest.include
  4. 80
      src/gtest/test_transaction_builder.cpp
  5. 169
      src/transaction_builder.cpp
  6. 73
      src/transaction_builder.h

4
depends/packages/librustzcash.mk

@ -3,8 +3,8 @@ $(package)_version=0.1
$(package)_download_path=https://github.com/zcash/$(package)/archive/
$(package)_file_name=$(package)-$($(package)_git_commit).tar.gz
$(package)_download_file=$($(package)_git_commit).tar.gz
$(package)_sha256_hash=5a50aae38a9ef4823cd319278ad95706a129cc091e1cca9342802f1ff75aba15
$(package)_git_commit=93e26d1d8716ac88f8bb372d442315edcd2deabd
$(package)_sha256_hash=86139e8a6cc76ae1a04ed8229beef760de1beb2a72fbe15450b44c3649d48a5d
$(package)_git_commit=32026ea0a13337548f1b6e57d99f0b7b6b9d0d81
$(package)_dependencies=rust $(rust_crates)
$(package)_patches=cargo.config

2
src/Makefile.am

@ -199,6 +199,7 @@ BITCOIN_CORE_H = \
timedata.h \
tinyformat.h \
torcontrol.h \
transaction_builder.h \
txdb.h \
txmempool.h \
ui_interface.h \
@ -384,6 +385,7 @@ libbitcoin_common_a_SOURCES = \
script/script_error.cpp \
script/sign.cpp \
script/standard.cpp \
transaction_builder.cpp \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)

1
src/Makefile.gtest.include

@ -34,6 +34,7 @@ zcash_gtest_SOURCES += \
gtest/test_rpc.cpp \
gtest/test_sapling_note.cpp \
gtest/test_transaction.cpp \
gtest/test_transaction_builder.cpp \
gtest/test_upgrades.cpp \
gtest/test_validation.cpp \
gtest/test_circuit.cpp \

80
src/gtest/test_transaction_builder.cpp

@ -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);
}

169
src/transaction_builder.cpp

@ -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);
}

73
src/transaction_builder.h

@ -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…
Cancel
Save