Compare commits
6 Commits
8a4a2063a2
...
3be05964d7
Author | SHA1 | Date |
---|---|---|
Conrado Gouvea | 3be05964d7 | |
Kris Nuttycombe | 24fb0c97df | |
Jack Grigg | 4eee55d4de | |
Jack Grigg | e9625d2483 | |
Jack Grigg | c0736d3ef6 | |
Jack Grigg | 96d6ee0b8f |
|
@ -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]
|
||||
|
|
|
@ -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",
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -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" }
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
33
src/main.cpp
33
src/main.cpp
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue