Update ZIP 244 implementation
This brings in the changes that align the transparent parts of ZIP 244 with BIP 341.
This commit is contained in:
parent
067b3b3032
commit
96d6ee0b8f
|
@ -3,7 +3,7 @@ replace-with = "vendored-sources"
|
||||||
|
|
||||||
[source."https://github.com/zcash/librustzcash.git"]
|
[source."https://github.com/zcash/librustzcash.git"]
|
||||||
git = "https://github.com/zcash/librustzcash.git"
|
git = "https://github.com/zcash/librustzcash.git"
|
||||||
rev = "5622b060b1f57de7afc3d0b4e425b9b4b22482a0"
|
rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7"
|
||||||
replace-with = "vendored-sources"
|
replace-with = "vendored-sources"
|
||||||
|
|
||||||
[source.vendored-sources]
|
[source.vendored-sources]
|
||||||
|
|
|
@ -501,7 +501,7 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equihash"
|
name = "equihash"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -510,7 +510,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "f4jumble"
|
name = "f4jumble"
|
||||||
version = "0.0.0"
|
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 = [
|
dependencies = [
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
]
|
]
|
||||||
|
@ -858,6 +858,7 @@ dependencies = [
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"zcash_address",
|
"zcash_address",
|
||||||
|
"zcash_encoding",
|
||||||
"zcash_history",
|
"zcash_history",
|
||||||
"zcash_note_encryption",
|
"zcash_note_encryption",
|
||||||
"zcash_primitives",
|
"zcash_primitives",
|
||||||
|
@ -1917,7 +1918,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_address"
|
name = "zcash_address"
|
||||||
version = "0.0.0"
|
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 = [
|
dependencies = [
|
||||||
"bech32",
|
"bech32",
|
||||||
"bs58",
|
"bs58",
|
||||||
|
@ -1928,7 +1929,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_encoding"
|
name = "zcash_encoding"
|
||||||
version = "0.0.0"
|
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 = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"nonempty",
|
"nonempty",
|
||||||
|
@ -1937,7 +1938,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_history"
|
name = "zcash_history"
|
||||||
version = "0.2.0"
|
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 = [
|
dependencies = [
|
||||||
"bigint",
|
"bigint",
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
|
@ -1947,7 +1948,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_note_encryption"
|
name = "zcash_note_encryption"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
|
@ -1958,7 +1959,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_primitives"
|
name = "zcash_primitives"
|
||||||
version = "0.5.0"
|
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 = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"bip0039",
|
"bip0039",
|
||||||
|
@ -1994,7 +1995,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.5.0"
|
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 = [
|
dependencies = [
|
||||||
"bellman",
|
"bellman",
|
||||||
"blake2b_simd 1.0.0",
|
"blake2b_simd 1.0.0",
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -45,6 +45,7 @@ tracing = "0.1"
|
||||||
tracing-core = "0.1"
|
tracing-core = "0.1"
|
||||||
tracing-appender = "0.2"
|
tracing-appender = "0.2"
|
||||||
zcash_address = "0.0"
|
zcash_address = "0.0"
|
||||||
|
zcash_encoding = "0.0"
|
||||||
zcash_history = "0.2"
|
zcash_history = "0.2"
|
||||||
zcash_note_encryption = "0.1"
|
zcash_note_encryption = "0.1"
|
||||||
zcash_primitives = { version = "0.5", features = ["transparent-inputs"] }
|
zcash_primitives = { version = "0.5", features = ["transparent-inputs"] }
|
||||||
|
@ -72,8 +73,9 @@ codegen-units = 1
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
|
hdwallet = { git = "https://github.com/nuttycom/hdwallet", rev = "576683b9f2865f1118c309017ff36e01f84420c9" }
|
||||||
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
|
zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
|
zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
|
zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
|
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "4f4a25252f0fc6b84c44316d810107bc7ea7f32a" }
|
zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
|
zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "ff243b4f0055d89d3abb526e234688a09c3d8cb7" }
|
||||||
|
|
|
@ -48,7 +48,10 @@ static void ECDSA(benchmark::State& state)
|
||||||
mtx.vout[i].scriptPubKey = CScript() << OP_1;
|
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
|
// sign all inputs
|
||||||
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
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 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);
|
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,10 @@ void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranch
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||||
if (dataToBeSigned == one) {
|
if (dataToBeSigned == one) {
|
||||||
throw std::runtime_error("SignatureHash failed");
|
throw std::runtime_error("SignatureHash failed");
|
||||||
|
@ -535,7 +538,11 @@ TEST(ContextualCheckShieldedInputsTest, BadTxnsInvalidJoinsplitSignature) {
|
||||||
CMutableTransaction mtx = GetValidTransaction();
|
CMutableTransaction mtx = GetValidTransaction();
|
||||||
mtx.joinSplitSig.bytes[0] += 1;
|
mtx.joinSplitSig.bytes[0] += 1;
|
||||||
CTransaction tx(mtx);
|
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;
|
MockCValidationState state;
|
||||||
// during initial block download, for transactions being accepted into the
|
// 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.
|
// Create a valid transaction for the Sapling epoch.
|
||||||
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
|
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
|
||||||
CTransaction tx(mtx);
|
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;
|
MockCValidationState state;
|
||||||
// Ensure that the transaction validates against Sapling.
|
// Ensure that the transaction validates against Sapling.
|
||||||
|
@ -603,10 +614,14 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
|
||||||
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
|
auto saplingBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
|
||||||
CMutableTransaction mtx = GetValidTransaction(saplingBranchId);
|
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
|
// Check that the signature is valid before we add L
|
||||||
{
|
{
|
||||||
CTransaction tx(mtx);
|
CTransaction tx(mtx);
|
||||||
const PrecomputedTransactionData txdata(tx);
|
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||||
MockCValidationState state;
|
MockCValidationState state;
|
||||||
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, orchardAuth, consensus, saplingBranchId, false, true));
|
EXPECT_TRUE(ContextualCheckShieldedInputs(tx, txdata, state, orchardAuth, consensus, saplingBranchId, false, true));
|
||||||
}
|
}
|
||||||
|
@ -626,7 +641,7 @@ TEST(ContextualCheckShieldedInputsTest, NonCanonicalEd25519Signature) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransaction tx(mtx);
|
CTransaction tx(mtx);
|
||||||
const PrecomputedTransactionData txdata(tx);
|
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||||
|
|
||||||
MockCValidationState state;
|
MockCValidationState state;
|
||||||
// during initial block download, for transactions being accepted into the
|
// during initial block download, for transactions being accepted into the
|
||||||
|
@ -1299,7 +1314,10 @@ TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
|
||||||
|
|
||||||
// Coinbase transaction does not pass shielded input checks, as bindingSig
|
// Coinbase transaction does not pass shielded input checks, as bindingSig
|
||||||
// consensus rule is enforced.
|
// 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);
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-sapling-binding-signature-invalid", false, "")).Times(1);
|
||||||
ContextualCheckShieldedInputs(
|
ContextualCheckShieldedInputs(
|
||||||
tx, txdata, state, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true);
|
tx, txdata, state, orchardAuth, chainparams.GetConsensus(), heartwoodBranchId, false, true);
|
||||||
|
|
|
@ -129,7 +129,8 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
|
||||||
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
|
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
|
||||||
auto consensusBranchId = NetworkUpgradeInfo[idx].nBranchId;
|
auto consensusBranchId = NetworkUpgradeInfo[idx].nBranchId;
|
||||||
CValidationState state;
|
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));
|
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.
|
// Ensure that the inputs validate against Overwinter.
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
PrecomputedTransactionData txdata(tx);
|
PrecomputedTransactionData txdata(tx, {CTxOut(coinValue, scriptPubKey)});
|
||||||
EXPECT_TRUE(ContextualCheckInputs(
|
EXPECT_TRUE(ContextualCheckInputs(
|
||||||
tx, state, view, true, 0, false, txdata,
|
tx, state, view, true, 0, false, txdata,
|
||||||
consensusParams, overwinterBranchId));
|
consensusParams, overwinterBranchId));
|
||||||
|
|
21
src/main.cpp
21
src/main.cpp
|
@ -1974,7 +1974,11 @@ bool AcceptToMemoryPool(
|
||||||
|
|
||||||
// Check against previous transactions
|
// Check against previous transactions
|
||||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||||
PrecomputedTransactionData txdata(tx);
|
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))
|
if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -3190,6 +3194,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||||
REJECT_INVALID, "bad-blk-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 (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
|
@ -3209,13 +3220,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
return state.DoS(100, false, rejectCode, rejectReason);
|
return state.DoS(100, false, rejectCode, rejectReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& input : tx.vin) {
|
||||||
|
allPrevOutputs.push_back(view.GetOutputFor(input));
|
||||||
|
}
|
||||||
|
|
||||||
// insightexplorer
|
// insightexplorer
|
||||||
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2597
|
// https://github.com/bitpay/bitcoin/commit/017f548ea6d89423ef568117447e61dd5707ec42#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2597
|
||||||
if (fAddressIndex || fSpentIndex) {
|
if (fAddressIndex || fSpentIndex) {
|
||||||
for (size_t j = 0; j < tx.vin.size(); j++) {
|
for (size_t j = 0; j < tx.vin.size(); j++) {
|
||||||
|
|
||||||
const CTxIn input = tx.vin[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();
|
CScript::ScriptType scriptType = prevout.scriptPubKey.GetType();
|
||||||
const uint160 addrHash = prevout.scriptPubKey.AddressHash();
|
const uint160 addrHash = prevout.scriptPubKey.AddressHash();
|
||||||
if (fAddressIndex && scriptType != CScript::UNKNOWN) {
|
if (fAddressIndex && scriptType != CScript::UNKNOWN) {
|
||||||
|
@ -3250,7 +3265,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||||
REJECT_INVALID, "bad-blk-sigops");
|
REJECT_INVALID, "bad-blk-sigops");
|
||||||
}
|
}
|
||||||
|
|
||||||
txdata.emplace_back(tx);
|
txdata.emplace_back(tx, allPrevOutputs);
|
||||||
|
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
|
|
|
@ -219,7 +219,11 @@ public:
|
||||||
uint256 dataToBeSigned;
|
uint256 dataToBeSigned;
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
try {
|
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(
|
dataToBeSigned = SignatureHash(
|
||||||
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
|
scriptCode, mtx, NOT_AN_INPUT, SIGHASH_ALL, 0,
|
||||||
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()),
|
CurrentEpochBranchId(nHeight, chainparams.GetConsensus()),
|
||||||
|
@ -532,11 +536,16 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const MinerAddre
|
||||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||||
continue;
|
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()
|
// Note that flags: we don't want to set mempool/IsStandard()
|
||||||
// policy here, but we still have to ensure that the block we
|
// policy here, but we still have to ensure that the block we
|
||||||
// create only contains transactions that are valid in new blocks.
|
// create only contains transactions that are valid in new blocks.
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
PrecomputedTransactionData txdata(tx);
|
PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||||
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
|
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
|
||||||
continue;
|
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
|
// Script verification errors
|
||||||
UniValue vErrors(UniValue::VARR);
|
UniValue vErrors(UniValue::VARR);
|
||||||
|
|
||||||
// Use CTransaction for the constant parts of the
|
// Use CTransaction for the constant parts of the
|
||||||
// transaction to avoid rehashing.
|
// transaction to avoid rehashing.
|
||||||
const CTransaction txConst(mergedTx);
|
const CTransaction txConst(mergedTx);
|
||||||
const PrecomputedTransactionData txdata(txConst);
|
const PrecomputedTransactionData txdata(txConst, allPrevOutputs);
|
||||||
// Sign what we can:
|
// Sign what we can:
|
||||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||||
CTxIn& txin = mergedTx.vin[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.
|
/// Returns `nullptr` if the transaction is invalid, or a v1-v4 transaction format.
|
||||||
PrecomputedTxParts* zcash_transaction_precomputed_init(
|
PrecomputedTxParts* zcash_transaction_precomputed_init(
|
||||||
const unsigned char* txBytes,
|
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`.
|
/// Frees a precomputed transaction from `zcash_transaction_precomputed_init`.
|
||||||
void zcash_transaction_precomputed_free(PrecomputedTxParts* preTx);
|
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.
|
/// `sighash_ret` must point to a 32-byte array.
|
||||||
///
|
///
|
||||||
/// Returns `false` if any of the parameters are invalid; in this case,
|
/// Returns `false` if any of the parameters are invalid; in this case,
|
||||||
/// `sighash_ret` will be unaltered.
|
/// `sighash_ret` will be unaltered.
|
||||||
bool zcash_transaction_transparent_signature_digest(
|
bool zcash_transaction_zip244_signature_digest(
|
||||||
const PrecomputedTxParts* preTx,
|
const PrecomputedTxParts* preTx,
|
||||||
uint32_t sighashType,
|
uint32_t sighashType,
|
||||||
size_t index,
|
size_t index,
|
||||||
const unsigned char* scriptCode,
|
|
||||||
size_t scriptCode_len,
|
|
||||||
int64_t value,
|
|
||||||
unsigned char* sighash_ret);
|
unsigned char* sighash_ret);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::io::Cursor;
|
||||||
use std::{ptr, slice};
|
use std::{ptr, slice};
|
||||||
|
|
||||||
use blake2b_simd::Hash;
|
use blake2b_simd::Hash;
|
||||||
use libc::{c_uchar, size_t};
|
use libc::{c_uchar, size_t};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
use zcash_encoding::Vector;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::BranchId,
|
consensus::BranchId,
|
||||||
legacy::Script,
|
legacy::Script,
|
||||||
transaction::{
|
transaction::{
|
||||||
components::Amount,
|
components::{orchard as orchard_serialization, sapling, transparent, Amount},
|
||||||
sighash::{signature_hash, SignableInput},
|
sighash::{SignableInput, TransparentAuthorizingContext},
|
||||||
|
sighash_v5::v5_signature_hash,
|
||||||
txid::TxIdDigester,
|
txid::TxIdDigester,
|
||||||
Transaction, TxDigests, TxVersion,
|
Authorization, Transaction, TransactionData, TxDigests, TxVersion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,8 +59,97 @@ pub extern "C" fn zcash_transaction_digests(
|
||||||
true
|
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 {
|
pub struct PrecomputedTxParts {
|
||||||
tx: Transaction,
|
tx: TransactionData<PrecomputedAuth>,
|
||||||
txid_parts: TxDigests<Hash>,
|
txid_parts: TxDigests<Hash>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,10 +160,25 @@ pub struct PrecomputedTxParts {
|
||||||
pub extern "C" fn zcash_transaction_precomputed_init(
|
pub extern "C" fn zcash_transaction_precomputed_init(
|
||||||
tx_bytes: *const c_uchar,
|
tx_bytes: *const c_uchar,
|
||||||
tx_bytes_len: size_t,
|
tx_bytes_len: size_t,
|
||||||
|
all_prev_outputs: *const c_uchar,
|
||||||
|
all_prev_outputs_len: size_t,
|
||||||
) -> *mut PrecomputedTxParts {
|
) -> *mut PrecomputedTxParts {
|
||||||
let tx_bytes = unsafe { slice::from_raw_parts(tx_bytes, tx_bytes_len) };
|
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.
|
// 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) {
|
let tx = match Transaction::read(tx_bytes, BranchId::Canopy) {
|
||||||
Ok(tx) => tx,
|
Ok(tx) => tx,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -85,6 +193,28 @@ pub extern "C" fn zcash_transaction_precomputed_init(
|
||||||
ptr::null_mut()
|
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) => MapTransparent {
|
||||||
|
auth: TransparentAuth { all_prev_outputs },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx = tx
|
||||||
|
.into_data()
|
||||||
|
.map_authorization(f_transparent, IdentityMap, IdentityMap);
|
||||||
|
|
||||||
let txid_parts = tx.digest(TxIdDigester);
|
let txid_parts = tx.digest(TxIdDigester);
|
||||||
Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts }))
|
Box::into_raw(Box::new(PrecomputedTxParts { tx, txid_parts }))
|
||||||
}
|
}
|
||||||
|
@ -99,18 +229,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`
|
/// Returns `false` if any of the parameters are invalid; in this case, `sighash_ret`
|
||||||
/// will be unaltered.
|
/// will be unaltered.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn zcash_transaction_transparent_signature_digest(
|
pub extern "C" fn zcash_transaction_zip244_signature_digest(
|
||||||
precomputed_tx: *const PrecomputedTxParts,
|
precomputed_tx: *const PrecomputedTxParts,
|
||||||
hash_type: u32,
|
hash_type: u32,
|
||||||
index: size_t,
|
index: size_t,
|
||||||
script_code: *const c_uchar,
|
|
||||||
script_code_len: size_t,
|
|
||||||
value: i64,
|
|
||||||
sighash_ret: *mut [u8; 32],
|
sighash_ret: *mut [u8; 32],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let precomputed_tx = if let Some(res) = unsafe { precomputed_tx.as_ref() } {
|
let precomputed_tx = if let Some(res) = unsafe { precomputed_tx.as_ref() } {
|
||||||
|
@ -119,37 +254,56 @@ pub extern "C" fn zcash_transaction_transparent_signature_digest(
|
||||||
error!("Invalid precomputed transaction");
|
error!("Invalid precomputed transaction");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let script_code =
|
if matches!(
|
||||||
match Script::read(unsafe { slice::from_raw_parts(script_code, script_code_len) }) {
|
precomputed_tx.tx.version(),
|
||||||
Ok(res) => res,
|
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling,
|
||||||
Err(e) => {
|
) {
|
||||||
error!("Invalid scriptCode: {}", e);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let value = match Amount::from_i64(value) {
|
|
||||||
Ok(res) => res,
|
SignableInput::Transparent {
|
||||||
Err(()) => {
|
// This conversion to `u8` is always fine:
|
||||||
error!("Invalid amount");
|
// - We only call this FFI method once we already know we are using ZIP 244.
|
||||||
return false;
|
// - 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() {
|
// `v5_signature_hash` output is always 32 bytes.
|
||||||
TxVersion::Sprout(_) | TxVersion::Overwinter | TxVersion::Sapling => {
|
*unsafe { &mut *sighash_ret } = sighash.as_ref().try_into().unwrap();
|
||||||
// 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();
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1128,8 +1128,32 @@ uint256 GetShieldedOutputsHash(const CTransaction& txTo) {
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
|
||||||
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) :
|
PrecomputedTransactionData::PrecomputedTransactionData(
|
||||||
|
const CTransaction& txTo,
|
||||||
|
const std::vector<CTxOut>& allPrevOutputs) :
|
||||||
preTx(nullptr, zcash_transaction_precomputed_free)
|
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 =
|
bool isOverwinterV3 =
|
||||||
txTo.fOverwintered &&
|
txTo.fOverwintered &&
|
||||||
|
@ -1153,7 +1177,10 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||||
ss << txTo;
|
ss << txTo;
|
||||||
preTx.reset(zcash_transaction_precomputed_init(
|
preTx.reset(zcash_transaction_precomputed_init(
|
||||||
reinterpret_cast<const unsigned char*>(ss.data()),
|
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,36 @@ uint256 SignatureHash(
|
||||||
auto sigversion = SignatureHashVersion(txTo);
|
auto sigversion = SignatureHashVersion(txTo);
|
||||||
|
|
||||||
if (sigversion == SIGVERSION_ZIP244) {
|
if (sigversion == SIGVERSION_ZIP244) {
|
||||||
// The consensusBranchId parameter is ignored; we use the value stored
|
// ZIP 244, S.2: transparent_sig_digest
|
||||||
// in the transaction itself.
|
//
|
||||||
if (nIn == NOT_AN_INPUT) {
|
// 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.
|
// The signature digest is just the txid! No need to cross the FFI.
|
||||||
return txTo.GetHash();
|
return txTo.GetHash();
|
||||||
} else {
|
} else {
|
||||||
CDataStream sScriptCode(SER_NETWORK, PROTOCOL_VERSION);
|
// The amount parameter is ignored; we extract it from allPrevOutputs.
|
||||||
sScriptCode << *(CScriptBase*)(&scriptCode);
|
// - 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;
|
uint256 hash;
|
||||||
zcash_transaction_transparent_signature_digest(
|
if (!zcash_transaction_zip244_signature_digest(
|
||||||
txdata.preTx.get(),
|
txdata.preTx.get(),
|
||||||
nHashType,
|
nHashType,
|
||||||
nIn,
|
nIn,
|
||||||
reinterpret_cast<const unsigned char*>(sScriptCode.data()),
|
hash.begin()))
|
||||||
sScriptCode.size(),
|
{
|
||||||
amount,
|
throw std::logic_error("We should not reach here.");
|
||||||
hash.begin());
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
} else if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {
|
} else if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {
|
||||||
|
|
|
@ -98,7 +98,20 @@ struct PrecomputedTransactionData
|
||||||
/** Precomputed transaction parts. */
|
/** Precomputed transaction parts. */
|
||||||
std::unique_ptr<PrecomputedTxParts, decltype(&zcash_transaction_precomputed_free)> preTx;
|
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
|
enum SigVersion
|
||||||
|
|
|
@ -91,7 +91,10 @@ struct PrecomputedTransaction {
|
||||||
const CTransaction tx;
|
const CTransaction tx;
|
||||||
const PrecomputedTransactionData txdata;
|
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(
|
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);
|
set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
if (tx.nVersion >= ZIP225_TX_VERSION) {
|
||||||
|
set_error(err, zcash_script_ERR_TX_VERSION);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Deserializing the tx did not error.
|
// Deserializing the tx did not error.
|
||||||
set_error(err, zcash_script_ERR_OK);
|
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;
|
return preTx;
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
set_error(err, zcash_script_ERR_TX_DESERIALIZE); // Error deserializing
|
||||||
|
@ -166,10 +175,15 @@ int zcash_script_verify(
|
||||||
return set_error(err, zcash_script_ERR_TX_INDEX);
|
return set_error(err, zcash_script_ERR_TX_INDEX);
|
||||||
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen)
|
||||||
return set_error(err, zcash_script_ERR_TX_SIZE_MISMATCH);
|
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.
|
// Regardless of the verification result, the tx did not error.
|
||||||
set_error(err, zcash_script_ERR_OK);
|
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(
|
return VerifyScript(
|
||||||
tx.vin[nIn].scriptSig,
|
tx.vin[nIn].scriptSig,
|
||||||
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
|
CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen),
|
||||||
|
|
|
@ -41,6 +41,8 @@ typedef enum zcash_script_error_t
|
||||||
zcash_script_ERR_TX_INDEX,
|
zcash_script_ERR_TX_INDEX,
|
||||||
zcash_script_ERR_TX_SIZE_MISMATCH,
|
zcash_script_ERR_TX_SIZE_MISMATCH,
|
||||||
zcash_script_ERR_TX_DESERIALIZE,
|
zcash_script_ERR_TX_DESERIALIZE,
|
||||||
|
// Defined since API version 3.
|
||||||
|
zcash_script_ERR_TX_VERSION,
|
||||||
} zcash_script_error;
|
} zcash_script_error;
|
||||||
|
|
||||||
/** Script verification flags */
|
/** Script verification flags */
|
||||||
|
@ -54,6 +56,8 @@ enum
|
||||||
/// Deserializes the given transaction and precomputes values to improve
|
/// Deserializes the given transaction and precomputes values to improve
|
||||||
/// script verification performance.
|
/// 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
|
/// Returns a pointer to the precomputed transaction. Free this with
|
||||||
/// zcash_script_free_precomputed_tx once you are done.
|
/// zcash_script_free_precomputed_tx once you are done.
|
||||||
/// The precomputed transaction is guaranteed to not keep a reference to any
|
/// The precomputed transaction is guaranteed to not keep a reference to any
|
||||||
|
@ -91,6 +95,8 @@ EXPORT_SYMBOL int zcash_script_verify_precomputed(
|
||||||
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
|
/// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under
|
||||||
/// the additional constraints specified by flags.
|
/// 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.
|
/// 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
|
/// Note that script verification failure is indicated by err being set to
|
||||||
/// zcash_script_ERR_OK and a return value of 0.
|
/// zcash_script_ERR_OK and a return value of 0.
|
||||||
|
|
|
@ -157,7 +157,7 @@ BOOST_DATA_TEST_CASE(DoS_mapOrphans, boost::unit_test::data::xrange(static_cast<
|
||||||
tx.vout.resize(1);
|
tx.vout.resize(1);
|
||||||
tx.vout[0].nValue = 1*CENT;
|
tx.vout[0].nValue = 1*CENT;
|
||||||
tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
|
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);
|
SignSignature(keystore, txPrev, tx, txdata, 0, SIGHASH_ALL, consensusBranchId);
|
||||||
|
|
||||||
AddOrphanTx(tx, i);
|
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.n = j;
|
||||||
tx.vin[j].prevout.hash = txPrev.GetHash();
|
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);
|
SignSignature(keystore, txPrev, tx, txdata, 0, SIGHASH_ALL, consensusBranchId);
|
||||||
// Re-use same signature for other inputs
|
// Re-use same signature for other inputs
|
||||||
// (they don't have to be valid for this test)
|
// (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.n = i;
|
||||||
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
||||||
txTo[i].vout[0].nValue = 1;
|
txTo[i].vout[0].nValue = 1;
|
||||||
txdata.push_back(PrecomputedTransactionData(txTo[i]));
|
txdata.push_back(PrecomputedTransactionData(txTo[i], {txFrom.vout[i]}));
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<CKey> keys;
|
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.n = i;
|
||||||
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
||||||
txTo[i].vout[0].nValue = 1;
|
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++)
|
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.vin[0].scriptSig = scriptSig;
|
||||||
txTo.vout[0].nValue = 1;
|
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);
|
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].vin[0].prevout.hash = txFrom.GetHash();
|
||||||
txTo[i].vout[0].nValue = 1;
|
txTo[i].vout[0].nValue = 1;
|
||||||
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", 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 < 8; 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
|
// 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:
|
// Check to make sure signature verification fails if we use the wrong ScriptSig:
|
||||||
for (int i = 0; i < 8; i++) {
|
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++)
|
for (int j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
CScript sigSave = txTo[i].vin[0].scriptSig;
|
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].nValue = 1*CENT;
|
||||||
txTo[i].vout[0].scriptPubKey = inner[i];
|
txTo[i].vout[0].scriptPubKey = inner[i];
|
||||||
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", 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++)
|
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.vout[0].nValue = 10;
|
||||||
|
|
||||||
txTo.vin.resize(5);
|
txTo.vin.resize(5);
|
||||||
|
std::vector<CTxOut> allPrevOutputs;
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
txTo.vin[i].prevout.n = i;
|
txTo.vin[i].prevout.n = i;
|
||||||
txTo.vin[i].prevout.hash = txFrom.GetHash();
|
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, 0, SIGHASH_ALL, consensusBranchId));
|
||||||
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, txToData, 1, 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));
|
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;
|
ScriptError err;
|
||||||
CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
|
CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
|
||||||
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit);
|
CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit);
|
||||||
const PrecomputedTransactionData txdata(tx);
|
const PrecomputedTransactionData txdata(tx, txCredit.vout);
|
||||||
CMutableTransaction tx2 = tx;
|
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(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);
|
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)
|
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);
|
uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType, 0, consensusBranchId, txdata);
|
||||||
std::vector<unsigned char> vchSig, r, s;
|
std::vector<unsigned char> vchSig, r, s;
|
||||||
uint32_t iter = 0;
|
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 txFrom12 = BuildCreditingTransaction(scriptPubKey12);
|
||||||
CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12);
|
CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12);
|
||||||
const PrecomputedTransactionData txdata12(txTo12);
|
const PrecomputedTransactionData txdata12(txTo12, txFrom12.vout);
|
||||||
|
|
||||||
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12, txdata12, consensusBranchId);
|
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));
|
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 txFrom23 = BuildCreditingTransaction(scriptPubKey23);
|
||||||
CMutableTransaction txTo23 = BuildSpendingTransaction(CScript(), txFrom23);
|
CMutableTransaction txTo23 = BuildSpendingTransaction(CScript(), txFrom23);
|
||||||
const PrecomputedTransactionData txdata23(txTo23);
|
const PrecomputedTransactionData txdata23(txTo23, txFrom23.vout);
|
||||||
|
|
||||||
std::vector<CKey> keys;
|
std::vector<CKey> keys;
|
||||||
keys.push_back(key1); keys.push_back(key2);
|
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 txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID()));
|
||||||
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), txFrom);
|
CMutableTransaction txTo = BuildSpendingTransaction(CScript(), txFrom);
|
||||||
const PrecomputedTransactionData txToData(txTo);
|
const PrecomputedTransactionData txToData(txTo, txFrom.vout);
|
||||||
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
|
CScript& scriptPubKey = txFrom.vout[0].scriptPubKey;
|
||||||
CScript& scriptSig = txTo.vin[0].scriptSig;
|
CScript& scriptSig = txTo.vin[0].scriptSig;
|
||||||
|
|
||||||
|
|
|
@ -202,7 +202,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(tx);
|
CTransaction signTx(tx);
|
||||||
PrecomputedTransactionData txdata(signTx);
|
PrecomputedTransactionData txdata(signTx, {});
|
||||||
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||||
|
|
||||||
assert(ed25519_sign(
|
assert(ed25519_sign(
|
||||||
|
@ -237,7 +237,8 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
RandomScript(scriptCode);
|
RandomScript(scriptCode);
|
||||||
int nIn = insecure_rand() % txTo.vin.size();
|
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;
|
uint256 sh, sho;
|
||||||
sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
|
sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType);
|
||||||
|
@ -336,7 +337,8 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
|
||||||
continue;
|
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);
|
sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, consensusBranchId, txdata);
|
||||||
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
|
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(CheckTransaction(tx, state, verifier), strTest + comment);
|
||||||
BOOST_CHECK_MESSAGE(state.IsValid(), 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++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||||
|
@ -207,7 +209,9 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
fValid = CheckTransaction(tx, state, verifier) && state.IsValid();
|
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++)
|
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
|
||||||
{
|
{
|
||||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
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[0] = GetRandHash();
|
||||||
jsdesc->nullifiers[1] = 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(CheckTransactionWithoutProofVerification(newTx, state));
|
||||||
BOOST_CHECK(ContextualCheckTransaction(newTx, state, Params(), 0, true));
|
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;
|
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
|
// sign all inputs
|
||||||
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
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();
|
UniValue scriptCodesArr = test[4].get_array();
|
||||||
std::vector<CAmount> amounts;
|
std::vector<CAmount> amounts;
|
||||||
std::vector<CScript> scriptCodes;
|
std::vector<CScript> scriptCodes;
|
||||||
|
std::vector<CTxOut> allPrevOutputs;
|
||||||
if (tx.IsCoinBase()) {
|
if (tx.IsCoinBase()) {
|
||||||
BOOST_CHECK(amountsArr.empty());
|
BOOST_CHECK(amountsArr.empty());
|
||||||
BOOST_CHECK(scriptCodesArr.empty());
|
BOOST_CHECK(scriptCodesArr.empty());
|
||||||
|
@ -917,6 +928,7 @@ BOOST_AUTO_TEST_CASE(TxV5)
|
||||||
amounts.push_back(amountsArr[inpIdx].get_int64());
|
amounts.push_back(amountsArr[inpIdx].get_int64());
|
||||||
auto scriptCodeBytes = ParseHex(scriptCodesArr[inpIdx].get_str());
|
auto scriptCodeBytes = ParseHex(scriptCodesArr[inpIdx].get_str());
|
||||||
scriptCodes.push_back(CScript(scriptCodeBytes.begin(), scriptCodeBytes.end()));
|
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];
|
amount = amounts[nIn];
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrecomputedTransactionData txdata(tx);
|
const PrecomputedTransactionData txdata(tx, allPrevOutputs);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(
|
BOOST_CHECK_EQUAL(
|
||||||
SignatureHash(
|
SignatureHash(
|
||||||
|
|
|
@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
||||||
spends[i].vout[0].scriptPubKey = scriptPubKey;
|
spends[i].vout[0].scriptPubKey = scriptPubKey;
|
||||||
|
|
||||||
// Sign:
|
// Sign:
|
||||||
const PrecomputedTransactionData txdata(spends[i]);
|
const PrecomputedTransactionData txdata(spends[i], {coinbaseTxns[0].vout[0]});
|
||||||
std::vector<unsigned char> vchSig;
|
std::vector<unsigned char> vchSig;
|
||||||
uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, coinbaseTxns[0].vout[0].nValue, SPROUT_BRANCH_ID, txdata);
|
uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, coinbaseTxns[0].vout[0].nValue, SPROUT_BRANCH_ID, txdata);
|
||||||
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
|
||||||
|
|
|
@ -261,7 +261,7 @@ void TransactionBuilder::AddTransparentInput(COutPoint utxo, CScript scriptPubKe
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx.vin.emplace_back(utxo);
|
mtx.vin.emplace_back(utxo);
|
||||||
tIns.emplace_back(scriptPubKey, value);
|
tIns.emplace_back(value, scriptPubKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBuilder::AddTransparentOutput(const CTxDestination& to, CAmount value)
|
void TransactionBuilder::AddTransparentOutput(const CTxDestination& to, CAmount value)
|
||||||
|
@ -322,7 +322,7 @@ TransactionBuilderResult TransactionBuilder::Build()
|
||||||
change -= jsOutput.value;
|
change -= jsOutput.value;
|
||||||
}
|
}
|
||||||
for (auto tIn : tIns) {
|
for (auto tIn : tIns) {
|
||||||
change += tIn.value;
|
change += tIn.nValue;
|
||||||
}
|
}
|
||||||
for (auto tOut : mtx.vout) {
|
for (auto tOut : mtx.vout) {
|
||||||
change -= tOut.nValue;
|
change -= tOut.nValue;
|
||||||
|
@ -446,7 +446,7 @@ TransactionBuilderResult TransactionBuilder::Build()
|
||||||
//
|
//
|
||||||
|
|
||||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
|
auto consensusBranchId = CurrentEpochBranchId(nHeight, consensusParams);
|
||||||
const PrecomputedTransactionData txdata(mtx);
|
const PrecomputedTransactionData txdata(mtx, tIns);
|
||||||
|
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
uint256 dataToBeSigned;
|
uint256 dataToBeSigned;
|
||||||
|
@ -499,7 +499,7 @@ TransactionBuilderResult TransactionBuilder::Build()
|
||||||
SignatureData sigdata;
|
SignatureData sigdata;
|
||||||
bool signSuccess = ProduceSignature(
|
bool signSuccess = ProduceSignature(
|
||||||
TransactionSignatureCreator(
|
TransactionSignatureCreator(
|
||||||
keystore, &txNewConst, txdata, nIn, tIn.value, SIGHASH_ALL),
|
keystore, &txNewConst, txdata, nIn, tIn.nValue, SIGHASH_ALL),
|
||||||
tIn.scriptPubKey, sigdata, consensusBranchId);
|
tIn.scriptPubKey, sigdata, consensusBranchId);
|
||||||
|
|
||||||
if (!signSuccess) {
|
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 {
|
class TransactionBuilderResult {
|
||||||
private:
|
private:
|
||||||
std::optional<CTransaction> maybeTx;
|
std::optional<CTransaction> maybeTx;
|
||||||
|
@ -120,7 +111,7 @@ private:
|
||||||
std::vector<OutputDescriptionInfo> outputs;
|
std::vector<OutputDescriptionInfo> outputs;
|
||||||
std::vector<libzcash::JSInput> jsInputs;
|
std::vector<libzcash::JSInput> jsInputs;
|
||||||
std::vector<libzcash::JSOutput> jsOutputs;
|
std::vector<libzcash::JSOutput> jsOutputs;
|
||||||
std::vector<TransparentInputInfo> tIns;
|
std::vector<CTxOut> tIns;
|
||||||
|
|
||||||
std::optional<std::pair<uint256, libzcash::SaplingPaymentAddress>> saplingChangeAddr;
|
std::optional<std::pair<uint256, libzcash::SaplingPaymentAddress>> saplingChangeAddr;
|
||||||
std::optional<libzcash::SproutPaymentAddress> sproutChangeAddr;
|
std::optional<libzcash::SproutPaymentAddress> sproutChangeAddr;
|
||||||
|
|
|
@ -35,6 +35,8 @@ CMutableTransaction GetValidSproutReceiveTransaction(
|
||||||
}
|
}
|
||||||
mtx.vin[0].prevout.n = 0;
|
mtx.vin[0].prevout.n = 0;
|
||||||
mtx.vin[1].prevout.n = 0;
|
mtx.vin[1].prevout.n = 0;
|
||||||
|
std::vector<CTxOut> allPrevOutputs;
|
||||||
|
allPrevOutputs.resize(mtx.vin.size());
|
||||||
|
|
||||||
// Generate an ephemeral keypair.
|
// Generate an ephemeral keypair.
|
||||||
Ed25519SigningKey joinSplitPrivKey;
|
Ed25519SigningKey joinSplitPrivKey;
|
||||||
|
@ -69,7 +71,7 @@ CMutableTransaction GetValidSproutReceiveTransaction(
|
||||||
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||||
|
|
||||||
// Add the signature
|
// Add the signature
|
||||||
|
@ -185,7 +187,8 @@ CWalletTx GetValidSproutSpend(const libzcash::SproutSpendingKey& sk,
|
||||||
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||||
|
|
||||||
// Add the signature
|
// Add the signature
|
||||||
|
|
|
@ -834,7 +834,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_, txdata);
|
||||||
|
|
||||||
// Add the signature
|
// Add the signature
|
||||||
|
|
|
@ -361,7 +361,11 @@ UniValue AsyncRPCOperation_shieldcoinbase::perform_joinsplit(ShieldCoinbaseJSInf
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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);
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
||||||
|
|
||||||
// Add the signature
|
// Add the signature
|
||||||
|
|
|
@ -2761,6 +2761,9 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
if (!DecodeHexTx(tx, params[0].get_str()))
|
if (!DecodeHexTx(tx, params[0].get_str()))
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
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 inputs = params[1].get_obj();
|
||||||
UniValue outputs = params[2].get_obj();
|
UniValue outputs = params[2].get_obj();
|
||||||
|
@ -2889,7 +2892,8 @@ UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp)
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
CTransaction signTx(mtx);
|
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());
|
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
|
||||||
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId, txdata);
|
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
|
// Sign
|
||||||
int nIn = 0;
|
int nIn = 0;
|
||||||
CTransaction txNewConst(txNew);
|
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)
|
for (const std::pair<const CWalletTx*, unsigned int>& coin : setCoins)
|
||||||
{
|
{
|
||||||
bool signSuccess;
|
bool signSuccess;
|
||||||
|
|
|
@ -233,12 +233,14 @@ double benchmark_large_tx(size_t nInputs)
|
||||||
spending_tx.nVersion = SAPLING_TX_VERSION;
|
spending_tx.nVersion = SAPLING_TX_VERSION;
|
||||||
|
|
||||||
auto input_hash = orig_tx.GetHash();
|
auto input_hash = orig_tx.GetHash();
|
||||||
|
std::vector<CTxOut> allPrevOutputs;
|
||||||
// Add nInputs inputs
|
// Add nInputs inputs
|
||||||
for (size_t i = 0; i < nInputs; i++) {
|
for (size_t i = 0; i < nInputs; i++) {
|
||||||
spending_tx.vin.emplace_back(input_hash, 0);
|
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
|
// Sign for all the inputs
|
||||||
auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
|
auto consensusBranchId = NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId;
|
||||||
|
|
Loading…
Reference in New Issue