]> Git Repo - VerusCoin.git/commitdiff
TransactionBuilder with support for creating Sapling-only transactions
authorJack Grigg <[email protected]>
Tue, 17 Jul 2018 16:36:38 +0000 (10:36 -0600)
committerJack Grigg <[email protected]>
Wed, 25 Jul 2018 09:35:10 +0000 (11:35 +0200)
depends/packages/librustzcash.mk
src/Makefile.am
src/Makefile.gtest.include
src/gtest/test_transaction_builder.cpp [new file with mode: 0644]
src/transaction_builder.cpp [new file with mode: 0644]
src/transaction_builder.h [new file with mode: 0644]

index 3a338cc21d618892d4255cd8df591e513d8d4cd9..03c172dc924f8ad63f7fe217fa138c3bfd8bc2d4 100644 (file)
@@ -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
 
index cf5cc46cfbe66899e3cd5ff40b9f8359e0310947..804bc4865c6839e3bc821072f03aae7f832c4f51 100644 (file)
@@ -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)
 
index 519fe155209a4c30ad6b6440dedb4d51a41646dd..e089d57836f7c2621d4f187124f40d3ea68af693 100644 (file)
@@ -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 \
diff --git a/src/gtest/test_transaction_builder.cpp b/src/gtest/test_transaction_builder.cpp
new file mode 100644 (file)
index 0000000..9f56da1
--- /dev/null
@@ -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);
+}
diff --git a/src/transaction_builder.cpp b/src/transaction_builder.cpp
new file mode 100644 (file)
index 0000000..91de871
--- /dev/null
@@ -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);
+}
diff --git a/src/transaction_builder.h b/src/transaction_builder.h
new file mode 100644 (file)
index 0000000..e3d7096
--- /dev/null
@@ -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 */
This page took 0.039322 seconds and 4 git commands to generate.