Auto merge of #3646 - Eirik0:transaction-builder-result, r=daira
Return more information when building a transaction fails This PR is intended to make it easier to diagnose what went wrong when building a transaction using `TransactionBuilder` fails.
This commit is contained in:
commit
5862b2921b
|
@ -4,6 +4,7 @@
|
|||
#include "key_io.h"
|
||||
#include "main.h"
|
||||
#include "pubkey.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "transaction_builder.h"
|
||||
#include "zcash/Address.hpp"
|
||||
|
||||
|
@ -39,9 +40,7 @@ TEST(TransactionBuilder, Invoke)
|
|||
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
||||
builder1.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
|
||||
auto maybe_tx1 = builder1.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
|
||||
auto tx1 = maybe_tx1.get();
|
||||
auto tx1 = builder1.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx1.vin.size(), 1);
|
||||
EXPECT_EQ(tx1.vout.size(), 0);
|
||||
|
@ -69,14 +68,13 @@ TEST(TransactionBuilder, Invoke)
|
|||
// Create a Sapling-only transaction
|
||||
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
|
||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder2.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
// Check that trying to add a different anchor fails
|
||||
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
|
||||
// TODO: the following check can be split out in to another test
|
||||
ASSERT_THROW(builder2.AddSaplingSpend(expsk, note, uint256(), witness), UniValue);
|
||||
|
||||
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx2 = builder2.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||
auto tx2 = maybe_tx2.get();
|
||||
auto tx2 = builder2.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx2.vin.size(), 0);
|
||||
EXPECT_EQ(tx2.vout.size(), 0);
|
||||
|
@ -110,7 +108,7 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
|
|||
// Default CTxDestination type is an invalid address
|
||||
CTxDestination taddr;
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
EXPECT_FALSE(builder.AddTransparentOutput(taddr, 50));
|
||||
ASSERT_THROW(builder.AddTransparentOutput(taddr, 50), UniValue);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress)
|
||||
|
@ -121,7 +119,7 @@ TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress)
|
|||
// Default CTxDestination type is an invalid address
|
||||
CTxDestination taddr;
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
EXPECT_FALSE(builder.SendChangeTo(taddr));
|
||||
ASSERT_THROW(builder.SendChangeTo(taddr), UniValue);
|
||||
}
|
||||
|
||||
TEST(TransactionBuilder, FailsWithNegativeChange)
|
||||
|
@ -157,22 +155,22 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
|
|||
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
EXPECT_EQ("Change cannot be negative", builder.Build().GetError());
|
||||
|
||||
// Fail if there is only a transparent output
|
||||
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee
|
||||
builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
EXPECT_TRUE(builder.AddTransparentOutput(taddr, 50000));
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
builder.AddTransparentOutput(taddr, 50000);
|
||||
EXPECT_EQ("Change cannot be negative", builder.Build().GetError());
|
||||
|
||||
// Fails if there is insufficient input
|
||||
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in
|
||||
EXPECT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
EXPECT_EQ("Change cannot be negative", builder.Build().GetError());
|
||||
|
||||
// Succeeds if there is sufficient input
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 1);
|
||||
EXPECT_TRUE(static_cast<bool>(builder.Build()));
|
||||
EXPECT_TRUE(builder.Build().IsTx());
|
||||
|
||||
// Revert to default
|
||||
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||
|
@ -216,17 +214,15 @@ TEST(TransactionBuilder, ChangeOutput)
|
|||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||
EXPECT_EQ("Could not determine change address", builder.Build().GetError());
|
||||
}
|
||||
|
||||
// Change to the same address as the first Sapling spend
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
|
@ -241,9 +237,7 @@ TEST(TransactionBuilder, ChangeOutput)
|
|||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
builder.SendChangeTo(zChangeAddr, fvkOut.ovk);
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
|
@ -257,10 +251,8 @@ TEST(TransactionBuilder, ChangeOutput)
|
|||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||
ASSERT_TRUE(builder.SendChangeTo(taddr));
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
builder.SendChangeTo(taddr);
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 1);
|
||||
EXPECT_EQ(tx.vout.size(), 1);
|
||||
|
@ -300,11 +292,9 @@ TEST(TransactionBuilder, SetFee)
|
|||
// Default fee
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 0);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
|
@ -317,12 +307,10 @@ TEST(TransactionBuilder, SetFee)
|
|||
// Configured fee
|
||||
{
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
builder.SetFee(20000);
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx.vin.size(), 0);
|
||||
EXPECT_EQ(tx.vout.size(), 0);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "main.h"
|
||||
#include "pubkey.h"
|
||||
#include "rpc/protocol.h"
|
||||
#include "script/sign.h"
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
@ -20,6 +21,31 @@ SpendDescriptionInfo::SpendDescriptionInfo(
|
|||
librustzcash_sapling_generate_r(alpha.begin());
|
||||
}
|
||||
|
||||
TransactionBuilderResult::TransactionBuilderResult(const CTransaction& tx) : maybeTx(tx) {}
|
||||
|
||||
TransactionBuilderResult::TransactionBuilderResult(const std::string& error) : maybeError(error) {}
|
||||
|
||||
bool TransactionBuilderResult::IsTx() { return maybeTx != boost::none; }
|
||||
|
||||
bool TransactionBuilderResult::IsError() { return maybeError != boost::none; }
|
||||
|
||||
CTransaction TransactionBuilderResult::GetTxOrThrow() {
|
||||
if (maybeTx) {
|
||||
return maybeTx.get();
|
||||
} else {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction: " + GetError());
|
||||
}
|
||||
}
|
||||
|
||||
std::string TransactionBuilderResult::GetError() {
|
||||
if (maybeError) {
|
||||
return maybeError.get();
|
||||
} else {
|
||||
// This can only happen if isTx() is true in which case we should not call getError()
|
||||
throw std::runtime_error("getError() was called in TransactionBuilderResult, but the result was not initialized as an error.");
|
||||
}
|
||||
}
|
||||
|
||||
TransactionBuilder::TransactionBuilder(
|
||||
const Consensus::Params& consensusParams,
|
||||
int nHeight,
|
||||
|
@ -28,7 +54,7 @@ TransactionBuilder::TransactionBuilder(
|
|||
mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
|
||||
}
|
||||
|
||||
bool TransactionBuilder::AddSaplingSpend(
|
||||
void TransactionBuilder::AddSaplingSpend(
|
||||
libzcash::SaplingExpandedSpendingKey expsk,
|
||||
libzcash::SaplingNote note,
|
||||
uint256 anchor,
|
||||
|
@ -40,15 +66,12 @@ bool TransactionBuilder::AddSaplingSpend(
|
|||
}
|
||||
|
||||
// Consistency check: all anchors must equal the first one
|
||||
if (!spends.empty()) {
|
||||
if (spends[0].anchor != anchor) {
|
||||
return false;
|
||||
}
|
||||
if (spends.size() > 0 && spends[0].anchor != anchor) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Anchor does not match previously-added Sapling spends.");
|
||||
}
|
||||
|
||||
spends.emplace_back(expsk, note, anchor, witness);
|
||||
mtx.valueBalance += note.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransactionBuilder::AddSaplingOutput(
|
||||
|
@ -77,16 +100,15 @@ void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKe
|
|||
tIns.emplace_back(scriptPubKey, value);
|
||||
}
|
||||
|
||||
bool TransactionBuilder::AddTransparentOutput(CTxDestination& to, CAmount value)
|
||||
void TransactionBuilder::AddTransparentOutput(CTxDestination& to, CAmount value)
|
||||
{
|
||||
if (!IsValidDestination(to)) {
|
||||
return false;
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr.");
|
||||
}
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(to);
|
||||
CTxOut out(value, scriptPubKey);
|
||||
mtx.vout.push_back(out);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransactionBuilder::SetFee(CAmount fee)
|
||||
|
@ -100,19 +122,17 @@ void TransactionBuilder::SendChangeTo(libzcash::SaplingPaymentAddress changeAddr
|
|||
tChangeAddr = boost::none;
|
||||
}
|
||||
|
||||
bool TransactionBuilder::SendChangeTo(CTxDestination& changeAddr)
|
||||
void TransactionBuilder::SendChangeTo(CTxDestination& changeAddr)
|
||||
{
|
||||
if (!IsValidDestination(changeAddr)) {
|
||||
return false;
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid change address, not a valid taddr.");
|
||||
}
|
||||
|
||||
tChangeAddr = changeAddr;
|
||||
zChangeAddr = boost::none;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<CTransaction> TransactionBuilder::Build()
|
||||
TransactionBuilderResult TransactionBuilder::Build()
|
||||
{
|
||||
//
|
||||
// Consistency checks
|
||||
|
@ -127,7 +147,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
change -= tOut.nValue;
|
||||
}
|
||||
if (change < 0) {
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Change cannot be negative");
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -141,14 +161,14 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
AddSaplingOutput(zChangeAddr->first, zChangeAddr->second, change);
|
||||
} else if (tChangeAddr) {
|
||||
// tChangeAddr has already been validated.
|
||||
assert(AddTransparentOutput(tChangeAddr.value(), change));
|
||||
AddTransparentOutput(tChangeAddr.value(), change);
|
||||
} else if (!spends.empty()) {
|
||||
auto fvk = spends[0].expsk.full_viewing_key();
|
||||
auto note = spends[0].note;
|
||||
libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d);
|
||||
AddSaplingOutput(fvk.ovk, changeAddr, change);
|
||||
} else {
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Could not determine change address");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +185,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
spend.expsk.full_viewing_key(), spend.witness.position());
|
||||
if (!(cm && nf)) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Missing spend commitment or nullifier");
|
||||
}
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
@ -187,7 +207,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
sdesc.rk.begin(),
|
||||
sdesc.zkproof.data())) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Spend proof failed");
|
||||
}
|
||||
|
||||
sdesc.anchor = spend.anchor;
|
||||
|
@ -200,7 +220,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
auto cm = output.note.cm();
|
||||
if (!cm) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Missing output commitment");
|
||||
}
|
||||
|
||||
libzcash::SaplingNotePlaintext notePlaintext(output.note, output.memo);
|
||||
|
@ -208,7 +228,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
auto res = notePlaintext.encrypt(output.note.pk_d);
|
||||
if (!res) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Failed to encrypt note");
|
||||
}
|
||||
auto enc = res.get();
|
||||
auto encryptor = enc.second;
|
||||
|
@ -224,7 +244,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
odesc.cv.begin(),
|
||||
odesc.zkproof.begin())) {
|
||||
librustzcash_sapling_proving_ctx_free(ctx);
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Output proof failed");
|
||||
}
|
||||
|
||||
odesc.cm = *cm;
|
||||
|
@ -253,7 +273,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
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;
|
||||
return TransactionBuilderResult("Could not construct signature hash");
|
||||
}
|
||||
|
||||
// Create Sapling spendAuth and binding signatures
|
||||
|
@ -283,11 +303,11 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||
tIn.scriptPubKey, sigdata, consensusBranchId);
|
||||
|
||||
if (!signSuccess) {
|
||||
return boost::none;
|
||||
return TransactionBuilderResult("Failed to sign transaction");
|
||||
} else {
|
||||
UpdateTransaction(mtx, nIn, sigdata);
|
||||
}
|
||||
}
|
||||
|
||||
return CTransaction(mtx);
|
||||
return TransactionBuilderResult(CTransaction(mtx));
|
||||
}
|
||||
|
|
|
@ -52,6 +52,20 @@ struct TransparentInputInfo {
|
|||
CAmount value) : scriptPubKey(scriptPubKey), value(value) {}
|
||||
};
|
||||
|
||||
class TransactionBuilderResult {
|
||||
private:
|
||||
boost::optional<CTransaction> maybeTx;
|
||||
boost::optional<std::string> maybeError;
|
||||
public:
|
||||
TransactionBuilderResult() = delete;
|
||||
TransactionBuilderResult(const CTransaction& tx);
|
||||
TransactionBuilderResult(const std::string& error);
|
||||
bool IsTx();
|
||||
bool IsError();
|
||||
CTransaction GetTxOrThrow();
|
||||
std::string GetError();
|
||||
};
|
||||
|
||||
class TransactionBuilder
|
||||
{
|
||||
private:
|
||||
|
@ -74,9 +88,9 @@ public:
|
|||
|
||||
void SetFee(CAmount fee);
|
||||
|
||||
// Returns false if the anchor does not match the anchor used by
|
||||
// Throws if the anchor does not match the anchor used by
|
||||
// previously-added Sapling spends.
|
||||
bool AddSaplingSpend(
|
||||
void AddSaplingSpend(
|
||||
libzcash::SaplingExpandedSpendingKey expsk,
|
||||
libzcash::SaplingNote note,
|
||||
uint256 anchor,
|
||||
|
@ -91,13 +105,13 @@ public:
|
|||
// Assumes that the value correctly corresponds to the provided UTXO.
|
||||
void AddTransparentInput(COutPoint utxo, CScript scriptPubKey, CAmount value);
|
||||
|
||||
bool AddTransparentOutput(CTxDestination& to, CAmount value);
|
||||
void AddTransparentOutput(CTxDestination& to, CAmount value);
|
||||
|
||||
void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk);
|
||||
|
||||
bool SendChangeTo(CTxDestination& changeAddr);
|
||||
void SendChangeTo(CTxDestination& changeAddr);
|
||||
|
||||
boost::optional<CTransaction> Build();
|
||||
TransactionBuilderResult Build();
|
||||
};
|
||||
|
||||
#endif /* TRANSACTION_BUILDER_H */
|
||||
|
|
|
@ -338,13 +338,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||
if (!witnesses[i]) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Missing witness for Sapling note");
|
||||
}
|
||||
assert(builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].get()));
|
||||
builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].get());
|
||||
}
|
||||
|
||||
if (isToTaddr_) {
|
||||
if (!builder_.AddTransparentOutput(toTaddr_, sendAmount)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr.");
|
||||
}
|
||||
builder_.AddTransparentOutput(toTaddr_, sendAmount);
|
||||
} else {
|
||||
std::string zaddr = std::get<0>(recipient_);
|
||||
std::string memo = std::get<1>(recipient_);
|
||||
|
@ -375,11 +373,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||
|
||||
|
||||
// Build the transaction
|
||||
auto maybe_tx = builder_.Build();
|
||||
if (!maybe_tx) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction.");
|
||||
}
|
||||
tx_ = maybe_tx.get();
|
||||
tx_ = builder_.Build().GetTxOrThrow();
|
||||
|
||||
// Send the transaction
|
||||
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
|
||||
|
|
|
@ -416,7 +416,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
}
|
||||
|
||||
CTxDestination changeAddr = vchPubKey.GetID();
|
||||
assert(builder_.SendChangeTo(changeAddr));
|
||||
builder_.SendChangeTo(changeAddr);
|
||||
}
|
||||
|
||||
// Select Sapling notes
|
||||
|
@ -445,7 +445,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
if (!witnesses[i]) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Missing witness for Sapling note");
|
||||
}
|
||||
assert(builder_.AddSaplingSpend(expsk, notes[i], anchor, witnesses[i].get()));
|
||||
builder_.AddSaplingSpend(expsk, notes[i], anchor, witnesses[i].get());
|
||||
}
|
||||
|
||||
// Add Sapling outputs
|
||||
|
@ -469,17 +469,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
auto amount = std::get<1>(r);
|
||||
|
||||
auto address = DecodeDestination(outputAddress);
|
||||
if (!builder_.AddTransparentOutput(address, amount)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr.");
|
||||
}
|
||||
builder_.AddTransparentOutput(address, amount);
|
||||
}
|
||||
|
||||
// Build the transaction
|
||||
auto maybe_tx = builder_.Build();
|
||||
if (!maybe_tx) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction.");
|
||||
}
|
||||
tx_ = maybe_tx.get();
|
||||
tx_ = builder_.Build().GetTxOrThrow();
|
||||
|
||||
// Send the transaction
|
||||
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
|
||||
|
|
|
@ -274,11 +274,7 @@ bool ShieldToAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) c
|
|||
m_op->builder_.SendChangeTo(zaddr, ovk);
|
||||
|
||||
// Build the transaction
|
||||
auto maybe_tx = m_op->builder_.Build();
|
||||
if (!maybe_tx) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction.");
|
||||
}
|
||||
m_op->tx_ = maybe_tx.get();
|
||||
m_op->tx_ = m_op->builder_.Build().GetTxOrThrow();
|
||||
|
||||
// Send the transaction
|
||||
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
|
||||
|
|
|
@ -383,12 +383,10 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
|
|||
uint256 nullifier = nf.get();
|
||||
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
|
||||
builder.SetFee(0);
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
|
||||
|
@ -503,11 +501,9 @@ TEST(WalletTests, FindMySaplingNotes) {
|
|||
|
||||
// Generate transaction
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
// No Sapling notes can be found in tx which does not belong to the wallet
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
|
@ -643,11 +639,9 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||
|
||||
// Generate tx to create output note B
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 35000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
|
||||
// Fake-mine the transaction
|
||||
|
@ -699,19 +693,15 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||
|
||||
// Create transaction to spend note B
|
||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||
builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
|
||||
builder2.AddSaplingOutput(fvk.ovk, pk, 20000, {});
|
||||
auto maybe_tx2 = builder2.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||
auto tx2 = maybe_tx2.get();
|
||||
auto tx2 = builder2.Build().GetTxOrThrow();
|
||||
|
||||
// Create conflicting transaction which also spends note B
|
||||
auto builder3 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||
builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
|
||||
builder3.AddSaplingOutput(fvk.ovk, pk, 19999, {});
|
||||
auto maybe_tx3 = builder3.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx3), true);
|
||||
auto tx3 = maybe_tx3.get();
|
||||
auto tx3 = builder3.Build().GetTxOrThrow();
|
||||
|
||||
CWalletTx wtx2 {&wallet, tx2};
|
||||
CWalletTx wtx3 {&wallet, tx3};
|
||||
|
@ -808,11 +798,9 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
|
|||
|
||||
// Generate transaction
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
|
||||
|
@ -905,11 +893,9 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
|
|||
|
||||
// Generate transaction
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
|
||||
|
@ -1041,11 +1027,9 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||
|
||||
// Generate transaction, which sends funds to note B
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
ASSERT_TRUE(wallet.AddSaplingZKey(sk, pk));
|
||||
|
@ -1113,11 +1097,9 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||
|
||||
// Create transaction to spend note B
|
||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||
builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness);
|
||||
builder2.AddSaplingOutput(fvk.ovk, pk, 12500, {});
|
||||
auto maybe_tx2 = builder2.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||
auto tx2 = maybe_tx2.get();
|
||||
auto tx2 = builder2.Build().GetTxOrThrow();
|
||||
EXPECT_EQ(tx2.vin.size(), 0);
|
||||
EXPECT_EQ(tx2.vout.size(), 0);
|
||||
EXPECT_EQ(tx2.vjoinsplit.size(), 0);
|
||||
|
@ -1833,11 +1815,9 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
|
|||
|
||||
// Generate transaction
|
||||
auto builder = TransactionBuilder(consensusParams, 1);
|
||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk2, 25000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx = maybe_tx.get();
|
||||
auto tx = builder.Build().GetTxOrThrow();
|
||||
|
||||
// Wallet contains fvk1 but not fvk2
|
||||
CWalletTx wtx {&wallet, tx};
|
||||
|
@ -1985,9 +1965,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
|
|||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
||||
builder.AddSaplingOutput(fvk.ovk, pk, 40000, {});
|
||||
auto maybe_tx = builder.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||
auto tx1 = maybe_tx.get();
|
||||
auto tx1 = builder.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx1.vin.size(), 1);
|
||||
EXPECT_EQ(tx1.vout.size(), 0);
|
||||
|
@ -2040,11 +2018,9 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
|
|||
// Create a Sapling-only transaction
|
||||
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
|
||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
|
||||
builder2.AddSaplingSpend(expsk, note, anchor, witness);
|
||||
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||
auto maybe_tx2 = builder2.Build();
|
||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||
auto tx2 = maybe_tx2.get();
|
||||
auto tx2 = builder2.Build().GetTxOrThrow();
|
||||
|
||||
EXPECT_EQ(tx2.vin.size(), 0);
|
||||
EXPECT_EQ(tx2.vout.size(), 0);
|
||||
|
|
Loading…
Reference in New Issue