Compare commits

...

6 Commits

Author SHA1 Message Date
Conrado Gouvea 3be05964d7 Add zcash_script V5 functions
Co-authored-by: Jack Grigg <jack@z.cash>
2022-02-28 19:12:18 +00:00
Kris Nuttycombe 24fb0c97df
Merge pull request #5496 from str4d/5446-zip-244-updates
ZIP 244 updates
2022-02-25 07:34:08 -07:00
Jack Grigg 4eee55d4de ZIP 244: Address review comments 2022-02-17 22:41:41 +00:00
Jack Grigg e9625d2483 Enforce length constraints on allPrevOutputs 2022-02-17 22:41:41 +00:00
Jack Grigg c0736d3ef6 Enforce ZIP 244 consensus rules on sighash type
We implement this by throwing an exception in SignatureHash on violation
of the rules. For CHECKMULTISIG and CHECKSIG this results in `false`
being pushed onto the stack, while for CHECKMULTISIGVERIFY and
CHECKSIGVERIFY this results in an error.
2022-02-16 03:30:10 +00:00
Jack Grigg 96d6ee0b8f Update ZIP 244 implementation
This brings in the changes that align the transparent parts of ZIP 244
with BIP 341.
2022-02-16 03:29:42 +00:00
33 changed files with 762 additions and 139 deletions

View File

@ -3,7 +3,7 @@ replace-with = "vendored-sources"
[source."https://github.com/zcash/librustzcash.git"]
git = "https://github.com/zcash/librustzcash.git"
rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0"
rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7"
replace-with = "vendored-sources"
[source.vendored-sources]

17
Cargo.lock generated
View File

@ -501,7 +501,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "equihash"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"blake2b_simd 1.0.0",
"byteorder",
@ -510,7 +510,7 @@ dependencies = [
[[package]]
name = "f4jumble"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"blake2b_simd 1.0.0",
]
@ -858,6 +858,7 @@ dependencies = [
"tracing-core",
"tracing-subscriber",
"zcash_address",
"zcash_encoding",
"zcash_history",
"zcash_note_encryption",
"zcash_primitives",
@ -1917,7 +1918,7 @@ dependencies = [
[[package]]
name = "zcash_address"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"bech32",
"bs58",
@ -1928,7 +1929,7 @@ dependencies = [
[[package]]
name = "zcash_encoding"
version = "0.0.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"byteorder",
"nonempty",
@ -1937,7 +1938,7 @@ dependencies = [
[[package]]
name = "zcash_history"
version = "0.2.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"bigint",
"blake2b_simd 1.0.0",
@ -1947,7 +1948,7 @@ dependencies = [
[[package]]
name = "zcash_note_encryption"
version = "0.1.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"chacha20",
"chacha20poly1305",
@ -1958,7 +1959,7 @@ dependencies = [
[[package]]
name = "zcash_primitives"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"aes",
"bip0039",
@ -1994,7 +1995,7 @@ dependencies = [
[[package]]
name = "zcash_proofs"
version = "0.5.0"
source = "git+https://github.com/zcash/librustzcash.git?rev=4f4a25252f0fc6b84c44316d810107bc7ea7f32a#4f4a25252f0fc6b84c44316d810107bc7ea7f32a"
source = "git+https://github.com/zcash/librustzcash.git?rev=ff243b4f0055d89d3abb526e234688a09c3d8cb7#ff243b4f0055d89d3abb526e234688a09c3d8cb7"
dependencies = [
"bellman",
"blake2b_simd 1.0.0",

View File

@ -45,6 +45,7 @@ tracing = "0.1"
tracing-core = "0.1"
tracing-appender = "0.2"
zcash_address = "0.0"
zcash_encoding = "0.0"
zcash_history = "0.2"
zcash_note_encryption = "0.1"
zcash_primitives = { version = "0.5", features = ["transparent-inputs"] }
@ -72,8 +73,9 @@ codegen-units = 1
[patch.crates-io]
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }

View File

@ -50,6 +50,7 @@ zcash_gtest_SOURCES += \
gtest/test_random.cpp \
gtest/test_rpc.cpp \
gtest/test_sapling_note.cpp \
gtest/test_sighash.cpp \
gtest/test_timedata.cpp \
gtest/test_transaction.cpp \
gtest/test_transaction_builder.cpp \

View File

@ -48,7 +48,10 @@ static void ECDSA(benchmark::State& state)
mtx.vout[i].scriptPubKey = CScript() << OP_1;
}
const PrecomputedTransactionData txdata(mtx);
std::vector<CTxOut> allPrevOutputs;
// for benchmarking we simply use dummy inputs
allPrevOutputs.resize(mtx.vin.size());
const PrecomputedTransactionData txdata(mtx, allPrevOutputs);
// sign all inputs
for(uint32_t i = 0; i < mtx.vin.size(); i++) {

View File

@ -468,7 +468,11 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput)
}
const CKeyStore& keystore = tempKeystore;
const PrecomputedTransactionData txdata(mergedTx);
// We don't support v5 transactions via this API yet.
if (mergedTx.nVersion >= ZIP225_TX_VERSION) {
throw std::runtime_error("v5+ transactions not supported yet");
}
const PrecomputedTransactionData txdata(mergedTx, {});
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);

View File

@ -113,7 +113,10 @@ void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranch
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
const PrecomputedTransactionData txdata(signTx);
// Fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(signTx.vin.size());
const PrecomputedTransactionData txdata(signTx, allPrevOutputs);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
if (dataToBeSigned == one) {
throw std::runtime_error("SignatureHash failed");
@ -535,7 +538,11 @@ TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) {
CMutableTransaction mtx = GetValidTransaction();
mtx.joinSplitSig.bytes[0] += 1;
CTransaction tx(mtx);
const PrecomputedTransactionData txdata(tx);
// Recreate the fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(tx.vin.size());
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
MockCValidationState state;
// during initial block download, for transactions being accepted into the
@ -564,7 +571,11 @@ TEST(ContextualCheckShieldedInputsTest, JoinsplitSignatureDetectsOldBranchId) {
// Create a valid transaction for the Sapling epoch.
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
CTransaction tx(mtx);
const PrecomputedTransactionData txdata(tx);
// Recreate the fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(tx.vin.size());
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
MockCValidationState state;
// Ensure that the transaction validates against Sapling.
@ -603,10 +614,14 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
// Recreate the fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(mtx.vin.size());
// Check that the signature is valid before we add L
{
CTransaction tx(mtx);
const PrecomputedTransactionData txdata(tx);
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
MockCValidationState state;
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, orchardAuth, consensus, saplingBranchId, false, true));
}
@ -626,7 +641,7 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
}
CTransaction tx(mtx);
const PrecomputedTransactionData txdata(tx);
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
MockCValidationState state;
// during initial block download, for transactions being accepted into the
@ -1299,10 +1314,13 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
// Coinbase transaction does not pass shielded input checks, as bindingSig
// consensus rule is enforced.
PrecomputedTransactionData txdata(tx);
// - Note that coinbase txs don't have a previous output corresponding to
// their transparent input; ZIP 244 handles this by making the coinbase
// sighash the txid.
PrecomputedTransactionData txdata(tx, {});
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid", false, "")).Times(1);
ContextualCheckShieldedInputs(
tx, txdata, state, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true);
EXPECT_FALSE(ContextualCheckShieldedInputs(
tx, txdata, state, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true));
RegtestDeactivateHeartwood();
}

129
src/gtest/test_sighash.cpp Normal file
View File

@ -0,0 +1,129 @@
#include <gtest/gtest.h>
#include "random.h"
#include "script/interpreter.h"
#include "script/standard.h"
static std::pair<CMutableTransaction, std::vector<CTxOut>> DummyV5Transaction() {
auto key = CKey::TestOnlyRandomKey(true);
auto scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
// Create a fake pair of coins to spend.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(2);
allPrevOutputs[0].nValue = 1000;
allPrevOutputs[0].scriptPubKey = scriptPubKey;
allPrevOutputs[1].nValue = 2000;
allPrevOutputs[1].scriptPubKey = scriptPubKey;
// Create a fake 2-in 2-out transaction.
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersionGroupId = ZIP225_VERSION_GROUP_ID;
mtx.nVersion = ZIP225_TX_VERSION;
mtx.nConsensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_NU5].nBranchId;
mtx.vin.resize(allPrevOutputs.size());
mtx.vin[0].prevout.hash = GetRandHash();
mtx.vin[0].prevout.n = 0;
mtx.vin[0].prevout.hash = GetRandHash();
mtx.vin[0].prevout.n = 7;
mtx.vout.resize(2);
mtx.vout[0].nValue = 1500;
mtx.vout[0].scriptPubKey = scriptPubKey;
mtx.vout[1].nValue = 1500;
mtx.vout[1].scriptPubKey = scriptPubKey;
return std::make_pair(mtx, allPrevOutputs);
}
TEST(SigHashTest, Zip244AcceptsKnownHashTypes) {
auto parts = DummyV5Transaction();
auto mtx = parts.first;
auto allPrevOutputs = parts.second;
unsigned int nIn = 1;
PrecomputedTransactionData txdata(mtx, allPrevOutputs);
// These aren't used for ZIP 244 sighashes.
CScript scriptCode;
CAmount amount;
uint32_t consensusBranchId;
// Nothing should be thrown for known sighash types.
std::vector<uint8_t> knownSighashTypes {
SIGHASH_ALL,
SIGHASH_SINGLE,
SIGHASH_NONE,
SIGHASH_ANYONECANPAY | SIGHASH_ALL,
SIGHASH_ANYONECANPAY | SIGHASH_SINGLE,
SIGHASH_ANYONECANPAY | SIGHASH_NONE,
};
for (auto nHashType : knownSighashTypes) {
EXPECT_NO_THROW(SignatureHash(
scriptCode, mtx, nIn, nHashType, amount, consensusBranchId, txdata));
}
}
TEST(SigHashTest, Zip244RejectsUnknownHashTypes) {
auto parts = DummyV5Transaction();
auto mtx = parts.first;
auto allPrevOutputs = parts.second;
unsigned int nIn = 1;
PrecomputedTransactionData txdata(mtx, allPrevOutputs);
// These aren't used for ZIP 244 sighashes.
CScript scriptCode;
CAmount amount;
uint32_t consensusBranchId;
// An error should be thrown for unknown sighash types.
std::vector<uint8_t> unknownSighashTypes {
0, // Known in BIP 341, unknown in ZIP 244.
SIGHASH_SINGLE + 1,
0x7f,
0xff,
};
for (auto nHashType : unknownSighashTypes) {
EXPECT_THROW(
SignatureHash(scriptCode, mtx, nIn, nHashType, amount, consensusBranchId, txdata),
std::logic_error);
}
}
TEST(SigHashTest, Zip244RejectsSingleWithoutCorrespondingOutput) {
auto parts = DummyV5Transaction();
auto mtx = parts.first;
auto allPrevOutputs = parts.second;
// Modify the transaction to have only 1 output.
mtx.vout.resize(1);
unsigned int nIn = 1;
PrecomputedTransactionData txdata(mtx, allPrevOutputs);
// These aren't used for ZIP 244 sighashes.
CScript scriptCode;
CAmount amount;
uint32_t consensusBranchId;
// Nothing should be thrown for non-single sighash types.
std::vector<uint8_t> nonSighashSingleTypes {
SIGHASH_ALL,
SIGHASH_NONE,
SIGHASH_ANYONECANPAY | SIGHASH_ALL,
SIGHASH_ANYONECANPAY | SIGHASH_NONE,
};
for (auto nHashType : nonSighashSingleTypes) {
EXPECT_NO_THROW(SignatureHash(
scriptCode, mtx, nIn, nHashType, amount, consensusBranchId, txdata));
}
// SIGHASH_SINGLE types should throw an error.
std::vector<uint8_t> sighashSingleTypes {
SIGHASH_SINGLE,
SIGHASH_ANYONECANPAY | SIGHASH_SINGLE,
};
for (auto nHashType : sighashSingleTypes) {
EXPECT_THROW(
SignatureHash(scriptCode, mtx, nIn, nHashType, amount, consensusBranchId, txdata),
std::logic_error);
}
}

View File

@ -129,7 +129,8 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
auto consensusBranchId = NetworkUpgradeInfo[idx].nBranchId;
CValidationState state;
PrecomputedTransactionData txdata(tx);
// Coinbase transactions have one synthetic input with no prevout.
PrecomputedTransactionData txdata(tx, {});
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
}
}
@ -180,7 +181,7 @@ TEST(Validation, ContextualCheckInputsDetectsOldBranchId) {
// Ensure that the inputs validate against Overwinter.
CValidationState state;
PrecomputedTransactionData txdata(tx);
PrecomputedTransactionData txdata(tx, {CTxOut(coinValue, scriptPubKey)});
EXPECT_TRUE(ContextualCheckInputs(
tx, state, view, true, 0, false, txdata,
consensusParams, overwinterBranchId));

View File

@ -1265,7 +1265,7 @@ bool ContextualCheckShieldedInputs(
} catch (std::logic_error ex) {
// A logic error should never occur because we pass NOT_AN_INPUT and
// SIGHASH_ALL to SignatureHash().
return state.DoS(100, error("ContextualCheckTransaction(): error computing signature hash"),
return state.DoS(100, error("ContextualCheckShieldedInputs(): error computing signature hash"),
REJECT_INVALID, "error-computing-signature-hash");
}
}
@ -1289,7 +1289,7 @@ bool ContextualCheckShieldedInputs(
}
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckTransaction(): invalid joinsplit signature"),
error("ContextualCheckShieldedInputs(): invalid joinsplit signature"),
REJECT_INVALID, "bad-txns-invalid-joinsplit-signature");
}
}
@ -1319,7 +1319,7 @@ bool ContextualCheckShieldedInputs(
librustzcash_sapling_verification_ctx_free(ctx);
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckTransaction(): Sapling spend description invalid"),
error("ContextualCheckShieldedInputs(): Sapling spend description invalid"),
REJECT_INVALID, "bad-txns-sapling-spend-description-invalid");
}
}
@ -1337,7 +1337,7 @@ bool ContextualCheckShieldedInputs(
// This should be a non-contextual check, but we check it here
// as we need to pass over the outputs anyway in order to then
// call librustzcash_sapling_final_check().
return state.DoS(100, error("ContextualCheckTransaction(): Sapling output description invalid"),
return state.DoS(100, error("ContextualCheckShieldedInputs(): Sapling output description invalid"),
REJECT_INVALID, "bad-txns-sapling-output-description-invalid");
}
}
@ -1352,7 +1352,7 @@ bool ContextualCheckShieldedInputs(
librustzcash_sapling_verification_ctx_free(ctx);
return state.DoS(
dosLevelPotentiallyRelaxing,
error("ContextualCheckTransaction(): Sapling binding signature invalid"),
error("ContextualCheckShieldedInputs(): Sapling binding signature invalid"),
REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid");
}
@ -1973,8 +1973,12 @@ bool AcceptToMemoryPool(
}
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata(tx);
// This is done near the end to help prevent CPU exhaustion denial-of-service attacks.
std::vector<CTxOut> allPrevOutputs;
for (const auto& input : tx.vin) {
allPrevOutputs.push_back(view.GetOutputFor(input));
}
PrecomputedTransactionData txdata(tx, allPrevOutputs);
if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
{
return false;
@ -3190,6 +3194,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("ConnectBlock(): too many sigops"),
REJECT_INVALID, "bad-blk-sigops");
// Coinbase transactions are the only case where this vector will not be the same
// length as `tx.vin` (since coinbase transactions have a single synthetic input).
// Only shielded coinbase transactions will need to produce sighashes for coinbase
// transactions; this is handled in ZIP 244 by having the coinbase sighash be the
// txid.
std::vector<CTxOut> allPrevOutputs;
if (!tx.IsCoinBase())
{
if (!view.HaveInputs(tx))
@ -3209,13 +3220,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, false, rejectCode, rejectReason);
}
for (const auto& input : tx.vin) {
allPrevOutputs.push_back(view.GetOutputFor(input));
}
// insightexplorer
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2597
if (fAddressIndex || fSpentIndex) {
for (size_t j = 0; j < tx.vin.size(); j++) {
const CTxIn input = tx.vin[j];
const CTxOut &prevout = view.GetOutputFor(tx.vin[j]);
const CTxOut &prevout = allPrevOutputs[j];
CScript::ScriptType scriptType = prevout.scriptPubKey.GetType();
const uint160 addrHash = prevout.scriptPubKey.AddressHash();
if (fAddressIndex && scriptType != CScript::UNKNOWN) {
@ -3250,7 +3265,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
REJECT_INVALID, "bad-blk-sigops");
}
txdata.emplace_back(tx);
txdata.emplace_back(tx, allPrevOutputs);
if (!tx.IsCoinBase())
{

View File

@ -219,7 +219,11 @@ public:
uint256 dataToBeSigned;
CScript scriptCode;
try {
PrecomputedTransactionData txdata(mtx);
// This is a shielded coinbase transaction, so the sighash is either pre-v5
// and doesn't use the allPrevOutputs field of PrecomputedTransactionData), or
// v5+ and S.2 of ZIP 244 defers to T.2, causing allPrevOutputs to be ignored.
// We therefore can set it to the empty list here.
PrecomputedTransactionData txdata(mtx, {});
dataToBeSigned = SignatureHash(
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()),
@ -532,11 +536,16 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
std::vector<CTxOut> allPrevOutputs;
for (const auto& input : tx.vin) {
allPrevOutputs.push_back(view.GetOutputFor(input));
}
// Note that flags: we don't want to set mempool/IsStandard()
// policy here, but we still have to ensure that the block we
// create only contains transactions that are valid in new blocks.
CValidationState state;
PrecomputedTransactionData txdata(tx);
PrecomputedTransactionData txdata(tx, allPrevOutputs);
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
continue;

View File

@ -1037,13 +1037,25 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
}
}
std::vector<CTxOut> allPrevOutputs;
// We do not need to know the inputs for pre-v5 transactions.
// We can't sign v5+ transactions without knowing all inputs.
if (mergedTx.nVersion >= ZIP225_TX_VERSION) {
if (!view.HaveInputs(mergedTx)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot sign v5 transactions without knowing all inputs");
}
for (const auto& input : mergedTx.vin) {
allPrevOutputs.push_back(view.GetOutputFor(input));
}
}
// Script verification errors
UniValue vErrors(UniValue::VARR);
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
const CTransaction txConst(mergedTx);
const PrecomputedTransactionData txdata(txConst);
const PrecomputedTransactionData txdata(txConst, allPrevOutputs);
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];

View File

@ -39,24 +39,26 @@ bool zcash_transaction_digests(
/// Returns `nullptr` if the transaction is invalid, or a v1-v4 transaction format.
PrecomputedTxParts* zcash_transaction_precomputed_init(
const unsigned char* txBytes,
size_t txBytes_len);
size_t txBytes_len,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen);
/// Frees a precomputed transaction from `zcash_transaction_precomputed_init`.
void zcash_transaction_precomputed_free(PrecomputedTxParts* preTx);
/// Calculates a signature digest for the given transparent input.
/// Calculates a ZIP 244 signature digest for the given transaction.
///
/// `index` must be an index into the transaction's `vin`, or `NOT_AN_INPUT` for
/// calculating the signature digest for shielded signatures.
///
/// `sighash_ret` must point to a 32-byte array.
///
/// Returns `false` if any of the parameters are invalid; in this case,
/// `sighash_ret` will be unaltered.
bool zcash_transaction_transparent_signature_digest(
bool zcash_transaction_zip244_signature_digest(
const PrecomputedTxParts* preTx,
uint32_t sighashType,
size_t index,
const unsigned char* scriptCode,
size_t scriptCode_len,
int64_t value,
unsigned char* sighash_ret);
#ifdef __cplusplus

View File

@ -1,16 +1,20 @@
use std::convert::TryInto;
use std::io::Cursor;
use std::{ptr, slice};
use blake2b_simd::Hash;
use libc::{c_uchar, size_t};
use tracing::error;
use zcash_encoding::Vector;
use zcash_primitives::{
consensus::BranchId,
legacy::Script,
transaction::{
components::Amount,
sighash::{signature_hash, SignableInput},
components::{orchard as orchard_serialization, sapling, transparent, Amount},
sighash::{SignableInput, TransparentAuthorizingContext},
sighash_v5::v5_signature_hash,
txid::TxIdDigester,
Transaction, TxDigests, TxVersion,
Authorization, Transaction, TransactionData, TxDigests, TxVersion,
},
};
@ -55,8 +59,97 @@ pub extern "C" fn zcash_transaction_digests(
true
}
#[derive(Clone, Debug)]
struct TransparentAuth {
all_prev_outputs: Vec<transparent::TxOut>,
}
impl transparent::Authorization for TransparentAuth {
type ScriptSig = Script;
}
impl TransparentAuthorizingContext for TransparentAuth {
fn input_amounts(&self) -> Vec<Amount> {
self.all_prev_outputs
.iter()
.map(|prevout| prevout.value)
.collect()
}
fn input_scriptpubkeys(&self) -> Vec<Script> {
self.all_prev_outputs
.iter()
.map(|prevout| prevout.script_pubkey.clone())
.collect()
}
}
struct MapTransparent {
auth: TransparentAuth,
}
impl transparent::MapAuth<transparent::Authorized, TransparentAuth> for MapTransparent {
fn map_script_sig(
&self,
s: <transparent::Authorized as transparent::Authorization>::ScriptSig,
) -> <TransparentAuth as transparent::Authorization>::ScriptSig {
s
}
fn map_authorization(&self, _: transparent::Authorized) -> TransparentAuth {
// TODO: This map should consume self, so we can move self.auth
self.auth.clone()
}
}
// TODO: Move these trait impls into `zcash_primitives` so they can be on `()`.
struct IdentityMap;
impl sapling::MapAuth<sapling::Authorized, sapling::Authorized> for IdentityMap {
fn map_proof(
&self,
p: <sapling::Authorized as sapling::Authorization>::Proof,
) -> <sapling::Authorized as sapling::Authorization>::Proof {
p
}
fn map_auth_sig(
&self,
s: <sapling::Authorized as sapling::Authorization>::AuthSig,
) -> <sapling::Authorized as sapling::Authorization>::AuthSig {
s
}
fn map_authorization(&self, a: sapling::Authorized) -> sapling::Authorized {
a
}
}
impl orchard_serialization::MapAuth<orchard::bundle::Authorized, orchard::bundle::Authorized>
for IdentityMap
{
fn map_spend_auth(
&self,
s: <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth,
) -> <orchard::bundle::Authorized as orchard::bundle::Authorization>::SpendAuth {
s
}
fn map_authorization(&self, a: orchard::bundle::Authorized) -> orchard::bundle::Authorized {
a
}
}
struct PrecomputedAuth;
impl Authorization for PrecomputedAuth {
type TransparentAuth = TransparentAuth;
type SaplingAuth = sapling::Authorized;
type OrchardAuth = orchard::bundle::Authorized;
}
pub struct PrecomputedTxParts {
tx: Transaction,
tx: TransactionData<PrecomputedAuth>,
txid_parts: TxDigests<Hash>,
}
@ -67,10 +160,25 @@ pub struct PrecomputedTxParts {
pub extern "C" fn zcash_transaction_precomputed_init(
tx_bytes: *const c_uchar,
tx_bytes_len: size_t,
all_prev_outputs: *const c_uchar,
all_prev_outputs_len: size_t,
) -> *mut PrecomputedTxParts {
let tx_bytes = unsafe { slice::from_raw_parts(tx_bytes, tx_bytes_len) };
// We use a placeholder branch ID here, since it is not used for anything.
//
// TODO: This is also parsing a transaction that may have partially-filled fields.
// This doesn't matter for transparent components (the only such field would be the
// scriptSig fields of transparent inputs, which would serialize as empty Scripts),
// but is ill-defined for shielded components (we'll be serializing 64 bytes of zeroes
// for each signature). This is an internal FFI so it's fine for now, but we should
// refactor the transaction builder (which is the only source of partially-created
// shielded components) to use a different FFI for obtaining the sighash, that passes
// across the transaction components and then constructs the TransactionData. This is
// already being done as part of the Orchard changes to the transaction builder, since
// the Orchard bundle will already be built on the Rust side, and we can avoid passing
// it back and forward across the FFI with this change. We should similarly refactor
// the Sapling code to do the same.
let tx = match Transaction::read(tx_bytes, BranchId::Canopy) {
Ok(tx) => tx,
Err(e) => {
@ -85,6 +193,46 @@ pub extern "C" fn zcash_transaction_precomputed_init(
ptr::null_mut()
}
_ => {
let all_prev_outputs =
unsafe { slice::from_raw_parts(all_prev_outputs, all_prev_outputs_len) };
let mut cursor = Cursor::new(all_prev_outputs);
let f_transparent = match Vector::read(&mut cursor, transparent::TxOut::read) {
Err(e) => {
error!("Invalid all_prev_outputs field: {}", e);
return ptr::null_mut();
}
Ok(_) if (cursor.position() as usize) != all_prev_outputs.len() => {
error!("all_prev_outputs had trailing data");
return ptr::null_mut();
}
Ok(all_prev_outputs)
if !tx
.transparent_bundle()
.map(|t| {
if t.is_coinbase() {
// Coinbase txs have one fake input.
all_prev_outputs.is_empty()
} else {
// For non-coinbase txs, every input is real.
t.vin.len() == all_prev_outputs.len()
}
})
// If we have no transparent part, we should have no prev outputs.
.unwrap_or_else(|| all_prev_outputs.is_empty()) =>
{
error!("all_prev_outputs is incorrect length");
return ptr::null_mut();
}
Ok(all_prev_outputs) => MapTransparent {
auth: TransparentAuth { all_prev_outputs },
},
};
let tx = tx
.into_data()
.map_authorization(f_transparent, IdentityMap, IdentityMap);
let txid_parts = tx.digest(TxIdDigester);
Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts }))
}
@ -99,18 +247,23 @@ pub extern "C" fn zcash_transaction_precomputed_free(precomputed_tx: *mut Precom
}
}
/// Calculates a signature digest for the given transaction.
/// This MUST match `NOT_AN_INPUT` in `src/script/interpreter.h`.
const NOT_AN_INPUT: usize = 0xffffffff;
/// Calculates a ZIP 244 signature digest for the given transaction.
///
/// `index` must be an index into the transaction's `vin`, or `NOT_AN_INPUT` for
/// calculating the signature digest for shielded signatures.
///
/// `sighash_ret` must point to a 32-byte array.
///
/// Returns `false` if any of the parameters are invalid; in this case, `sighash_ret`
/// will be unaltered.
#[no_mangle]
pub extern "C" fn zcash_transaction_transparent_signature_digest(
pub extern "C" fn zcash_transaction_zip244_signature_digest(
precomputed_tx: *const PrecomputedTxParts,
hash_type: u32,
index: size_t,
script_code: *const c_uchar,
script_code_len: size_t,
value: i64,
sighash_ret: *mut [u8; 32],
) -> bool {
let precomputed_tx = if let Some(res) = unsafe { precomputed_tx.as_ref() } {
@ -119,37 +272,56 @@ pub extern "C" fn zcash_transaction_transparent_signature_digest(
error!("Invalid precomputed transaction");
return false;
};
let script_code =
match Script::read(unsafe { slice::from_raw_parts(script_code, script_code_len) }) {
Ok(res) => res,
Err(e) => {
error!("Invalid scriptCode: {}", e);
if matches!(
precomputed_tx.tx.version(),
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling,
) {
error!("Cannot calculate ZIP 244 digest for pre-v5 transaction");
return false;
}
let signable_input = if index == NOT_AN_INPUT {
SignableInput::Shielded
} else {
let prevout = match precomputed_tx.tx.transparent_bundle() {
Some(bundle) => match bundle.authorization.all_prev_outputs.get(index) {
Some(prevout) => prevout,
None => {
error!("nIn out of range");
return false;
}
},
None => {
error!("Tried to create a transparent sighash for a tx without transparent data");
return false;
}
};
let value = match Amount::from_i64(value) {
Ok(res) => res,
Err(()) => {
error!("Invalid amount");
return false;
SignableInput::Transparent {
// This conversion to `u8` is always fine:
// - We only call this FFI method once we already know we are using ZIP 244.
// - Even if we weren't, `hash_type` is one byte tacked onto the end of a
// signature, so it always fits into a `u8` (and TBH I don't know why we
// ever set it to `u32`).
hash_type: hash_type.try_into().unwrap(),
index,
// `script_code` is unused by `v5_signature_hash`, so instead of passing the
// real `script_code` across the FFI (and paying the serialization and parsing
// cost for no benefit), we set it to the prevout's `script_pubkey`. This
// happens to be correct anyway for every output script kind except P2SH.
script_code: &prevout.script_pubkey,
script_pubkey: &prevout.script_pubkey,
value: prevout.value,
}
};
let signable_input = SignableInput::transparent(index, &script_code, value);
let sighash = v5_signature_hash(
&precomputed_tx.tx,
&signable_input,
&precomputed_tx.txid_parts,
);
let sighash = match precomputed_tx.tx.version() {
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling => {
// We don't support these legacy transaction formats in this API.
return false;
}
_ => signature_hash(
&precomputed_tx.tx,
hash_type,
&signable_input,
&precomputed_tx.txid_parts,
),
};
*unsafe { &mut *sighash_ret } = *sighash.as_ref();
// `v5_signature_hash` output is always 32 bytes.
*unsafe { &mut *sighash_ret } = sighash.as_ref().try_into().unwrap();
true
}

View File

@ -1128,8 +1128,32 @@ uint256 GetShieldedOutputsHash(const CTransaction& txTo) {
} // anon namespace
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) :
PrecomputedTransactionData::PrecomputedTransactionData(
const CTransaction& txTo,
const std::vector<CTxOut>& allPrevOutputs) :
preTx(nullptr, zcash_transaction_precomputed_free)
{
CDataStream sAllPrevOutputs(SER_NETWORK, PROTOCOL_VERSION);
sAllPrevOutputs << allPrevOutputs;
SetPrecomputed(
txTo,
reinterpret_cast<const unsigned char*>(sAllPrevOutputs.data()),
sAllPrevOutputs.size());
}
PrecomputedTransactionData::PrecomputedTransactionData(
const CTransaction& txTo,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen) :
preTx(nullptr, zcash_transaction_precomputed_free)
{
SetPrecomputed(txTo, allPrevOutputs, allPrevOutputsLen);
}
void PrecomputedTransactionData::SetPrecomputed(
const CTransaction& txTo,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen)
{
bool isOverwinterV3 =
txTo.fOverwintered &&
@ -1153,7 +1177,10 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
ss << txTo;
preTx.reset(zcash_transaction_precomputed_init(
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size()));
ss.size(), allPrevOutputs, allPrevOutputsLen));
if (preTx == nullptr) {
throw std::ios_base::failure("Invalid arguments to PrecomputedTransactionData");
}
}
}
@ -1189,24 +1216,60 @@ uint256 SignatureHash(
auto sigversion = SignatureHashVersion(txTo);
if (sigversion == SIGVERSION_ZIP244) {
// The consensusBranchId parameter is ignored; we use the value stored
// in the transaction itself.
if (nIn == NOT_AN_INPUT) {
// ZIP 244, S.2: transparent_sig_digest
//
// If we are producing a hash for either a coinbase transaction, or a
// non-coinbase transaction that has no transparent inputs, the value of
// transparent_sig_digest is identical to the value specified in section
// T.2.
//
// https://zips.z.cash/zip-0244#s-2-transparent-sig-digest
if (txTo.IsCoinBase() || txTo.vin.empty()) {
// This situation only applies to shielded signatures.
assert(nIn == NOT_AN_INPUT);
assert(nHashType == SIGHASH_ALL);
// The signature digest is just the txid! No need to cross the FFI.
return txTo.GetHash();
} else {
CDataStream sScriptCode(SER_NETWORK, PROTOCOL_VERSION);
sScriptCode << *(CScriptBase*)(&scriptCode);
// S.2a: hash_type
//
// The following restrictions apply, which cause validation failure
// if violated:
// - Using any undefined hash_type (not 0x01, 0x02, 0x03, 0x81,
// 0x82, or 0x83).
// - Using SIGHASH_SINGLE without a "corresponding output" (an
// output with the same index as the input being verified).
switch (nHashType) {
case SIGHASH_SINGLE:
case SIGHASH_ANYONECANPAY | SIGHASH_SINGLE:
if (nIn >= txTo.vout.size()) {
throw std::logic_error(
"ZIP 244: Used SIGHASH_SINGLE without a corresponding output");
}
case SIGHASH_ALL:
case SIGHASH_NONE:
case SIGHASH_ANYONECANPAY | SIGHASH_ALL:
case SIGHASH_ANYONECANPAY | SIGHASH_NONE:
break;
default:
throw std::logic_error("ZIP 244: Used undefined hash_type");
}
// The amount parameter is ignored; we extract it from allPrevOutputs.
// - Note to future developers: if we ever replace the C++ logic for
// pre-v5 transactions with Rust logic, make sure signrawtransaction
// is updated to know about it!
// The consensusBranchId parameter is ignored; we use the value stored
// in the transaction itself.
uint256 hash;
zcash_transaction_transparent_signature_digest(
if (!zcash_transaction_zip244_signature_digest(
txdata.preTx.get(),
nHashType,
nIn,
reinterpret_cast<const unsigned char*>(sScriptCode.data()),
sScriptCode.size(),
amount,
hash.begin());
hash.begin()))
{
throw std::logic_error("We should not reach here.");
}
return hash;
}
} else if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {

View File

@ -98,7 +98,20 @@ struct PrecomputedTransactionData
/** Precomputed transaction parts. */
std::unique_ptr<PrecomputedTxParts, decltype(&zcash_transaction_precomputed_free)> preTx;
PrecomputedTransactionData(const CTransaction& tx);
PrecomputedTransactionData(
const CTransaction& tx,
const std::vector<CTxOut>& allPrevOutputs);
PrecomputedTransactionData(
const CTransaction& tx,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen);
private:
void SetPrecomputed(
const CTransaction& tx,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen);
};
enum SigVersion

View File

@ -91,7 +91,10 @@ struct PrecomputedTransaction {
const CTransaction tx;
const PrecomputedTransactionData txdata;
PrecomputedTransaction(CTransaction txIn) : tx(txIn), txdata(txIn) {}
PrecomputedTransaction(
CTransaction txIn,
const unsigned char* allPrevOutputs,
size_t allPrevOutputsLen) : tx(txIn), txdata(txIn, allPrevOutputs, allPrevOutputsLen) {}
};
void* zcash_script_new_precomputed_tx(
@ -107,10 +110,16 @@ void* zcash_script_new_precomputed_tx(
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
return nullptr;
}
if (tx.nVersion >= ZIP225_TX_VERSION) {
set_error(err, zcash_script_ERR_TX_VERSION);
return nullptr;
}
// Deserializing the tx did not error.
set_error(err, zcash_script_ERR_OK);
auto preTx = new PrecomputedTransaction(tx);
// This is a pre-v5 tx, so the PrecomputedTransactionData constructor
// field `allPrevOutputs` is not used.
auto preTx = new PrecomputedTransaction(tx, nullptr, 0);
return preTx;
} catch (const std::exception&) {
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
@ -118,6 +127,39 @@ void* zcash_script_new_precomputed_tx(
}
}
void* zcash_script_new_precomputed_tx_v5(
const unsigned char* txTo,
unsigned int txToLen,
const unsigned char* allPrevOutputs,
unsigned int allPrevOutputsLen,
zcash_script_error* err)
{
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
CTransaction tx;
stream >> tx;
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) {
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
return nullptr;
}
try {
auto preTx = new PrecomputedTransaction(tx, allPrevOutputs, allPrevOutputsLen);
// Deserializing the tx did not error.
set_error(err, zcash_script_ERR_OK);
return preTx;
} catch (const std::exception&) {
// We had some error when parsing allPrevOutputs inside the
// PrecomputedTransactionData constructor.
set_error(err, zcash_script_ERR_ALL_PREV_OUTPUTS_DESERIALIZE);
return nullptr;
}
} catch (const std::exception&) {
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
return nullptr;
}
}
void zcash_script_free_precomputed_tx(void* pre_preTx)
{
PrecomputedTransaction* preTx = static_cast<PrecomputedTransaction*>(pre_preTx);
@ -166,10 +208,15 @@ int zcash_script_verify(
return set_error(err, zcash_script_ERR_TX_INDEX);
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
return set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
if (tx.nVersion >= ZIP225_TX_VERSION) {
return set_error(err, zcash_script_ERR_TX_VERSION);
}
// Regardless of the verification result, the tx did not error.
set_error(err, zcash_script_ERR_OK);
PrecomputedTransactionData txdata(tx);
// This is a pre-v5 tx, so the PrecomputedTransactionData constructor
// field `allPrevOutputs` is not used.
PrecomputedTransactionData txdata(tx, {});
return VerifyScript(
tx.vin[nIn].scriptSig,
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
@ -182,6 +229,53 @@ int zcash_script_verify(
}
}
int zcash_script_verify_v5(
const unsigned char* txTo,
unsigned int txToLen,
const unsigned char* allPrevOutputs,
unsigned int allPrevOutputsLen,
unsigned int nIn,
unsigned int flags,
uint32_t consensusBranchId,
zcash_script_error* err)
{
try {
TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen);
CTransaction tx;
stream >> tx;
if (nIn >= tx.vin.size())
return set_error(err, zcash_script_ERR_TX_INDEX);
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
return set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
// TODO: we can swap this second deserialization for an FFI call by
// fetching this through PrecomputedTransactionData. Simplicity for now.
CDataStream sAllPrevOutputs(
reinterpret_cast<const char*>(allPrevOutputs),
reinterpret_cast<const char*>(allPrevOutputs + allPrevOutputsLen),
SER_NETWORK,
PROTOCOL_VERSION);
std::vector<CTxOut> prevOutputs;
sAllPrevOutputs >> prevOutputs;
if (!(tx.IsCoinBase() ? prevOutputs.empty() : tx.vin.size() == prevOutputs.size())) {
return set_error(err, zcash_script_ERR_ALL_PREV_OUTPUTS_SIZE_MISMATCH);
}
// Regardless of the verification result, the tx did not error.
set_error(err, zcash_script_ERR_OK);
PrecomputedTransactionData txdata(tx, allPrevOutputs, allPrevOutputsLen);
return VerifyScript(
tx.vin[nIn].scriptSig,
prevOutputs[nIn].scriptPubKey,
flags,
TransactionSignatureChecker(&tx, txdata, nIn, prevOutputs[nIn].nValue),
consensusBranchId,
NULL);
} catch (const std::exception&) {
return set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
}
}
unsigned int zcash_script_legacy_sigop_count_precomputed(
const void* pre_preTx,
zcash_script_error* err)

View File

@ -33,7 +33,7 @@
extern "C" {
#endif
#define ZCASH_SCRIPT_API_VER 2
#define ZCASH_SCRIPT_API_VER 3
typedef enum zcash_script_error_t
{
@ -41,6 +41,10 @@ typedef enum zcash_script_error_t
zcash_script_ERR_TX_INDEX,
zcash_script_ERR_TX_SIZE_MISMATCH,
zcash_script_ERR_TX_DESERIALIZE,
// Defined since API version 3.
zcash_script_ERR_TX_VERSION,
zcash_script_ERR_ALL_PREV_OUTPUTS_SIZE_MISMATCH,
zcash_script_ERR_ALL_PREV_OUTPUTS_DESERIALIZE,
} zcash_script_error;
/** Script verification flags */
@ -54,6 +58,8 @@ enum
/// Deserializes the given transaction and precomputes values to improve
/// script verification performance.
///
/// This API cannot be used for v5+ transactions, and will return an error.
///
/// Returns a pointer to the precomputed transaction. Free this with
/// zcash_script_free_precomputed_tx once you are done.
/// The precomputed transaction is guaranteed to not keep a reference to any
@ -66,6 +72,27 @@ void* zcash_script_new_precomputed_tx(
unsigned int txToLen,
zcash_script_error* err);
/// Deserializes the given transaction and precomputes values to improve
/// script verification performance. Must be used for V5 transactions;
/// may also be used for previous versions.
///
/// allPrevOutputs must point to the encoding of the vector of all outputs
/// from previous transactions that are spent by the inputs of the given transaction.
/// The outputs must be encoded as specified by Bitcoin. The full encoding will thus
/// consist of a CompactSize prefix containing the number of outputs, followed by
/// the concatenated outputs, each of them encoded as (value, CompactSize, pk_script).
///
/// Returns a pointer to the precomputed transaction. Free this with
/// zcash_script_free_precomputed_tx once you are done.
///
/// If not NULL, err will contain an error/success code for the operation.
void* zcash_script_new_precomputed_tx_v5(
const unsigned char* txTo,
unsigned int txToLen,
const unsigned char* allPrevOutputs,
unsigned int allPrevOutputsLen,
zcash_script_error* err);
/// Frees a precomputed transaction previously created with
/// zcash_script_new_precomputed_tx.
void zcash_script_free_precomputed_tx(void* preTx);
@ -91,6 +118,8 @@ EXPORT_SYMBOL int zcash_script_verify_precomputed(
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
/// the additional constraints specified by flags.
///
/// This API cannot be used for v5+ transactions, and will return an error.
///
/// If not NULL, err will contain an error/success code for the operation.
/// Note that script verification failure is indicated by err being set to
/// zcash_script_ERR_OK and a return value of 0.
@ -102,6 +131,30 @@ EXPORT_SYMBOL int zcash_script_verify(
uint32_t consensusBranchId,
zcash_script_error* err);
/// Returns 1 if the input nIn of the serialized transaction pointed to by
/// txTo correctly spends the matching output in allPrevOutputs under
/// the additional constraints specified by flags. Must be used for V5 transactions;
/// may also be used for previous versions.
///
/// allPrevOutputs must point to the encoding of the vector of all outputs
/// from previous transactions that are spent by the inputs of the given transaction.
/// The outputs must be encoded as specified by Bitcoin. The full encoding will thus
/// consist of a CompactSize prefix containing the number of outputs, followed by
/// the concatenated outputs, each of them encoded as (value, CompactSize, pk_script).
///
/// If not NULL, err will contain an error/success code for the operation.
/// Note that script verification failure is indicated by err being set to
/// zcash_script_ERR_OK and a return value of 0.
EXPORT_SYMBOL int zcash_script_verify_v5(
const unsigned char* txTo,
unsigned int txToLen,
const unsigned char* allPrevOutputs,
unsigned int allPrevOutputsLen,
unsigned int nIn,
unsigned int flags,
uint32_t consensusBranchId,
zcash_script_error* err);
/// Returns the number of transparent signature operations in the
/// transparent inputs and outputs of the precomputed transaction
/// pointed to by preTx.

View File

@ -157,7 +157,7 @@ BOOST_DATA_TEST_CASE(DoS_mapOrphans, boost::unit_test::data::xrange(static_cast<
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
const PrecomputedTransactionData txdata(tx);
const PrecomputedTransactionData txdata(tx, {txPrev.vout[0]});
SignSignature(keystore, txPrev, tx, txdata, 0, SIGHASH_ALL, consensusBranchId);
AddOrphanTx(tx, i);
@ -178,7 +178,10 @@ BOOST_DATA_TEST_CASE(DoS_mapOrphans, boost::unit_test::data::xrange(static_cast<
tx.vin[j].prevout.n = j;
tx.vin[j].prevout.hash = txPrev.GetHash();
}
const PrecomputedTransactionData txdata(tx);
// Fake the coins being spent (the random orphan doesn't actually have them all).
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(tx.vin.size(), txPrev.vout[0]);
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
SignSignature(keystore, txPrev, tx, txdata, 0, SIGHASH_ALL, consensusBranchId);
// Re-use same signature for other inputs
// (they don't have to be valid for this test)

View File

@ -79,7 +79,7 @@ BOOST_DATA_TEST_CASE(multisig_verify, boost::unit_test::data::xrange(static_cast
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
txdata.push_back(PrecomputedTransactionData(txTo[i]));
txdata.push_back(PrecomputedTransactionData(txTo[i], {txFrom.vout[i]}));
}
vector<CKey> keys;
@ -225,7 +225,7 @@ BOOST_DATA_TEST_CASE(multisig_Sign, boost::unit_test::data::xrange(static_cast<i
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
txdata.push_back(PrecomputedTransactionData(txTo[i]));
txdata.push_back(PrecomputedTransactionData(txTo[i], {txFrom.vout[i]}));
}
for (int i = 0; i < 3; i++)

View File

@ -47,7 +47,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri
txTo.vin[0].scriptSig = scriptSig;
txTo.vout[0].nValue = 1;
const PrecomputedTransactionData txdata(txTo);
const PrecomputedTransactionData txdata(txTo, txFrom.vout);
return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, txdata, 0, txFrom.vout[0].nValue), consensusBranchId, &err);
}
@ -108,7 +108,7 @@ BOOST_DATA_TEST_CASE(sign, boost::unit_test::data::xrange(static_cast<int>(Conse
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
txToData.push_back(PrecomputedTransactionData(txTo[i]));
txToData.push_back(PrecomputedTransactionData(txTo[i], {txFrom.vout[i]}));
}
for (int i = 0; i < 8; i++)
{
@ -117,7 +117,7 @@ BOOST_DATA_TEST_CASE(sign, boost::unit_test::data::xrange(static_cast<int>(Conse
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++) {
PrecomputedTransactionData txdata(txTo[i]);
PrecomputedTransactionData txdata(txTo[i], {txFrom.vout[i]});
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
@ -212,7 +212,7 @@ BOOST_DATA_TEST_CASE(set, boost::unit_test::data::xrange(static_cast<int>(Consen
txTo[i].vout[0].nValue = 1*CENT;
txTo[i].vout[0].scriptPubKey = inner[i];
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
txToData.push_back(PrecomputedTransactionData(txTo[i]));
txToData.push_back(PrecomputedTransactionData(txTo[i], {txFrom.vout[i]}));
}
for (int i = 0; i < 4; i++)
{
@ -347,12 +347,14 @@ BOOST_DATA_TEST_CASE(AreInputsStandard, boost::unit_test::data::xrange(static_ca
txTo.vout[0].nValue = 10;
txTo.vin.resize(5);
std::vector<CTxOut> allPrevOutputs;
for (int i = 0; i < 5; i++)
{
txTo.vin[i].prevout.n = i;
txTo.vin[i].prevout.hash = txFrom.GetHash();
allPrevOutputs.push_back(txFrom.vout[i]);
}
const PrecomputedTransactionData txToData(txTo);
const PrecomputedTransactionData txToData(txTo, allPrevOutputs);
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, txToData, 0, SIGHASH_ALL, consensusBranchId));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, txToData, 1, SIGHASH_ALL, consensusBranchId));
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, txToData, 2, SIGHASH_ALL, consensusBranchId));

View File

@ -78,7 +78,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, ui
ScriptError err;
CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit);
const PrecomputedTransactionData txdata(tx);
const PrecomputedTransactionData txdata(tx, txCredit.vout);
CMutableTransaction tx2 = tx;
BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, MutableTransactionSignatureChecker(&tx, txdata, 0, txCredit.vout[0].nValue), consensusBranchId, &err) == expect, message);
BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message);
@ -231,7 +231,7 @@ public:
TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32)
{
const PrecomputedTransactionData txdata(spendTx);
const PrecomputedTransactionData txdata(spendTx, creditTx.vout);
uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType, 0, consensusBranchId, txdata);
std::vector<unsigned char> vchSig, r, s;
uint32_t iter = 0;
@ -712,7 +712,7 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG12, boost::unit_test::data::xrange(stat
CMutableTransaction txFrom12 = BuildCreditingTransaction(scriptPubKey12);
CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12);
const PrecomputedTransactionData txdata12(txTo12);
const PrecomputedTransactionData txdata12(txTo12, txFrom12.vout);
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12, txdata12, consensusBranchId);
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, txdata12, 0, txFrom12.vout[0].nValue), consensusBranchId, &err));
@ -746,7 +746,7 @@ BOOST_DATA_TEST_CASE(script_CHECKMULTISIG23, boost::unit_test::data::xrange(stat
CMutableTransaction txFrom23 = BuildCreditingTransaction(scriptPubKey23);
CMutableTransaction txTo23 = BuildSpendingTransaction(CScript(), txFrom23);
const PrecomputedTransactionData txdata23(txTo23);
const PrecomputedTransactionData txdata23(txTo23, txFrom23.vout);
std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2);
@ -822,7 +822,7 @@ BOOST_DATA_TEST_CASE(script_combineSigs, boost::unit_test::data::xrange(static_c
CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), txFrom);
const PrecomputedTransactionData txToData(txTo);
const PrecomputedTransactionData txToData(txTo, txFrom.vout);
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
CScript& scriptSig = txTo.vin[0].scriptSig;

View File

@ -202,7 +202,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co
// Empty output script.
CScript scriptCode;
CTransaction signTx(tx);
PrecomputedTransactionData txdata(signTx);
PrecomputedTransactionData txdata(signTx, {});
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
assert(ed25519_sign(
@ -237,7 +237,8 @@ BOOST_AUTO_TEST_CASE(sighash_test)
CScript scriptCode;
RandomScript(scriptCode);
int nIn = insecure_rand() % txTo.vin.size();
const PrecomputedTransactionData txdata(txTo);
// We don't generate v5 transactions here; we have separate ZIP 244 test vectors.
const PrecomputedTransactionData txdata(txTo, {});
uint256 sh, sho;
sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
@ -336,7 +337,8 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
continue;
}
const PrecomputedTransactionData txdata(tx);
// These test vectors do not include v5 transactions.
const PrecomputedTransactionData txdata(tx, {});
sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, consensusBranchId, txdata);
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
}

View File

@ -115,7 +115,9 @@ BOOST_AUTO_TEST_CASE(tx_valid)
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, verifier), strTest + comment);
BOOST_CHECK_MESSAGE(state.IsValid(), comment);
PrecomputedTransactionData txdata(tx);
// None of these test vectors use ZIP 244.
assert(tx.nVersion < ZIP225_TX_VERSION);
PrecomputedTransactionData txdata(tx, {});
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
@ -207,7 +209,9 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
CValidationState state;
fValid = CheckTransaction(tx, state, verifier) && state.IsValid();
PrecomputedTransactionData txdata(tx);
// None of these test vectors use ZIP 244.
assert(tx.nVersion < ZIP225_TX_VERSION);
PrecomputedTransactionData txdata(tx, {});
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
{
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
@ -451,7 +455,10 @@ void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransa
jsdesc->nullifiers[0] = GetRandHash();
jsdesc->nullifiers[1] = GetRandHash();
const PrecomputedTransactionData txdata(newTx);
// Fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(newTx.vin.size());
const PrecomputedTransactionData txdata(newTx, allPrevOutputs);
BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state));
BOOST_CHECK(ContextualCheckTransaction(newTx, state, Params(), 0, true));
@ -672,7 +679,10 @@ BOOST_AUTO_TEST_CASE(test_big_overwinter_transaction) {
mtx.vout[i].scriptPubKey = CScript() << OP_1;
}
PrecomputedTransactionData txdata(mtx);
// Fake coins being spent.
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(mtx.vin.size());
PrecomputedTransactionData txdata(mtx, allPrevOutputs);
// sign all inputs
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
@ -906,6 +916,7 @@ BOOST_AUTO_TEST_CASE(TxV5)
UniValue scriptCodesArr = test[4].get_array();
std::vector<CAmount> amounts;
std::vector<CScript> scriptCodes;
std::vector<CTxOut> allPrevOutputs;
if (tx.IsCoinBase()) {
BOOST_CHECK(amountsArr.empty());
BOOST_CHECK(scriptCodesArr.empty());
@ -917,6 +928,7 @@ BOOST_AUTO_TEST_CASE(TxV5)
amounts.push_back(amountsArr[inpIdx].get_int64());
auto scriptCodeBytes = ParseHex(scriptCodesArr[inpIdx].get_str());
scriptCodes.push_back(CScript(scriptCodeBytes.begin(), scriptCodeBytes.end()));
allPrevOutputs.emplace_back(amounts[inpIdx], scriptCodes[inpIdx]);
}
}
@ -933,7 +945,7 @@ BOOST_AUTO_TEST_CASE(TxV5)
amount = amounts[nIn];
}
const PrecomputedTransactionData txdata(tx);
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
BOOST_CHECK_EQUAL(
SignatureHash(

View File

@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
spends[i].vout[0].scriptPubKey = scriptPubKey;
// Sign:
const PrecomputedTransactionData txdata(spends[i]);
const PrecomputedTransactionData txdata(spends[i], {coinbaseTxns[0].vout[0]});
std::vector<unsigned char> vchSig;
uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, coinbaseTxns[0].vout[0].nValue, SPROUT_BRANCH_ID, txdata);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));

View File

@ -261,7 +261,7 @@ void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKe
}
mtx.vin.emplace_back(utxo);
tIns.emplace_back(scriptPubKey, value);
tIns.emplace_back(value, scriptPubKey);
}
void TransactionBuilder::AddTransparentOutput(const CTxDestination& to, CAmount value)
@ -322,7 +322,7 @@ TransactionBuilderResult TransactionBuilder::Build()
change -= jsOutput.value;
}
for (auto tIn : tIns) {
change += tIn.value;
change += tIn.nValue;
}
for (auto tOut : mtx.vout) {
change -= tOut.nValue;
@ -446,7 +446,7 @@ TransactionBuilderResult TransactionBuilder::Build()
//
auto consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
const PrecomputedTransactionData txdata(mtx);
const PrecomputedTransactionData txdata(mtx, tIns);
// Empty output script.
uint256 dataToBeSigned;
@ -499,7 +499,7 @@ TransactionBuilderResult TransactionBuilder::Build()
SignatureData sigdata;
bool signSuccess = ProduceSignature(
TransactionSignatureCreator(
keystore, &txNewConst, txdata, nIn, tIn.value, SIGHASH_ALL),
keystore, &txNewConst, txdata, nIn, tIn.nValue, SIGHASH_ALL),
tIn.scriptPubKey, sigdata, consensusBranchId);
if (!signSuccess) {

View File

@ -81,15 +81,6 @@ struct JSDescriptionInfo {
);
};
struct TransparentInputInfo {
CScript scriptPubKey;
CAmount value;
TransparentInputInfo(
CScript scriptPubKey,
CAmount value) : scriptPubKey(scriptPubKey), value(value) {}
};
class TransactionBuilderResult {
private:
std::optional<CTransaction> maybeTx;
@ -120,7 +111,7 @@ private:
std::vector<OutputDescriptionInfo> outputs;
std::vector<libzcash::JSInput> jsInputs;
std::vector<libzcash::JSOutput> jsOutputs;
std::vector<TransparentInputInfo> tIns;
std::vector<CTxOut> tIns;
std::optional<std::pair<uint256, libzcash::SaplingPaymentAddress>> saplingChangeAddr;
std::optional<libzcash::SproutPaymentAddress> sproutChangeAddr;

View File

@ -35,6 +35,8 @@ CMutableTransaction GetValidSproutReceiveTransaction(
}
mtx.vin[0].prevout.n = 0;
mtx.vin[1].prevout.n = 0;
std::vector<CTxOut> allPrevOutputs;
allPrevOutputs.resize(mtx.vin.size());
// Generate an ephemeral keypair.
Ed25519SigningKey joinSplitPrivKey;
@ -69,7 +71,7 @@ CMutableTransaction GetValidSproutReceiveTransaction(
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
CScript scriptCode;
CTransaction signTx(mtx);
const PrecomputedTransactionData txdata(signTx);
const PrecomputedTransactionData txdata(signTx, allPrevOutputs);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
// Add the signature
@ -185,7 +187,8 @@ CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk,
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
CScript scriptCode;
CTransaction signTx(mtx);
const PrecomputedTransactionData txdata(signTx);
assert(signTx.vin.size() == 0);
const PrecomputedTransactionData txdata(signTx, {});
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
// Add the signature

View File

@ -834,7 +834,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
PrecomputedTransactionData txdata(signTx);
std::vector<CTxOut> allPrevOutputs;
for (const MergeToAddressInputUTXO& t : utxoInputs_) {
allPrevOutputs.emplace_back(std::get<1>(t), std::get<2>(t));
}
PrecomputedTransactionData txdata(signTx, allPrevOutputs);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_, txdata);
// Add the signature

View File

@ -361,7 +361,11 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
PrecomputedTransactionData txdata(signTx);
std::vector<CTxOut> allPrevOutputs;
for (ShieldCoinbaseUTXO & t : inputs_) {
allPrevOutputs.emplace_back(t.amount, t.scriptPubKey);
}
PrecomputedTransactionData txdata(signTx, allPrevOutputs);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
// Add the signature

View File

@ -2761,6 +2761,9 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
CTransaction tx;
if (!DecodeHexTx(tx, params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
if (tx.nVersion >= ZIP225_TX_VERSION) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "v5+ transactions do not support Sprout");
}
UniValue inputs = params[1].get_obj();
UniValue outputs = params[2].get_obj();
@ -2889,7 +2892,8 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
PrecomputedTransactionData txdata(signTx);
// This API will never support v5+ transactions, and can ignore ZIP 244.
PrecomputedTransactionData txdata(signTx, {});
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);

View File

@ -4767,7 +4767,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Sign
int nIn = 0;
CTransaction txNewConst(txNew);
const PrecomputedTransactionData txdata(txNewConst);
std::vector<CTxOut> allPrevOutputs;
for (const std::pair<const CWalletTx*, unsigned int>& coin : setCoins) {
allPrevOutputs.push_back(coin.first->vout[coin.second]);
}
const PrecomputedTransactionData txdata(txNewConst, allPrevOutputs);
for (const std::pair<const CWalletTx*, unsigned int>& coin : setCoins)
{
bool signSuccess;

View File

@ -233,12 +233,14 @@ double benchmark_large_tx(size_t nInputs)
spending_tx.nVersion = SAPLING_TX_VERSION;
auto input_hash = orig_tx.GetHash();
std::vector<CTxOut> allPrevOutputs;
// Add nInputs inputs
for (size_t i = 0; i < nInputs; i++) {
spending_tx.vin.emplace_back(input_hash, 0);
allPrevOutputs.push_back(orig_tx.vout[0]);
}
PrecomputedTransactionData txdata(spending_tx);
PrecomputedTransactionData txdata(spending_tx, allPrevOutputs);
// Sign for all the inputs
auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;