We do not need to be able to calculate multiple SignatureHash versions for a single transaction format; instead, we use the transaction format to determine the SigVersion. The consensus branch ID *does* need to be passed in from the outside, as only the caller knows the context in which the SignatureHash is being calculated (ie. mempool acceptance vs. block validation). JoinSplit signature verification has been moved into ContextualCheckTransaction, where the consensus branch ID can be obtained. The argument to the sign command for zcash-tx has been modified to take a height in addition to the optional sigtype flags.
149 lines
5.2 KiB
C++
149 lines
5.2 KiB
C++
// Copyright (c) 2016 The Zcash developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "utiltest.h"
|
|
|
|
#include "consensus/upgrades.h"
|
|
|
|
CWalletTx GetValidReceive(ZCJoinSplit& params,
|
|
const libzcash::SpendingKey& sk, CAmount value,
|
|
bool randomInputs) {
|
|
CMutableTransaction mtx;
|
|
mtx.nVersion = 2; // Enable JoinSplits
|
|
mtx.vin.resize(2);
|
|
if (randomInputs) {
|
|
mtx.vin[0].prevout.hash = GetRandHash();
|
|
mtx.vin[1].prevout.hash = GetRandHash();
|
|
} else {
|
|
mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
|
mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
|
}
|
|
mtx.vin[0].prevout.n = 0;
|
|
mtx.vin[1].prevout.n = 0;
|
|
|
|
// Generate an ephemeral keypair.
|
|
uint256 joinSplitPubKey;
|
|
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
|
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
|
|
mtx.joinSplitPubKey = joinSplitPubKey;
|
|
|
|
boost::array<libzcash::JSInput, 2> inputs = {
|
|
libzcash::JSInput(), // dummy input
|
|
libzcash::JSInput() // dummy input
|
|
};
|
|
|
|
boost::array<libzcash::JSOutput, 2> outputs = {
|
|
libzcash::JSOutput(sk.address(), value),
|
|
libzcash::JSOutput(sk.address(), value)
|
|
};
|
|
|
|
boost::array<libzcash::Note, 2> output_notes;
|
|
|
|
// Prepare JoinSplits
|
|
uint256 rt;
|
|
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt,
|
|
inputs, outputs, 2*value, 0, false};
|
|
mtx.vjoinsplit.push_back(jsdesc);
|
|
|
|
// Empty output script.
|
|
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
|
CScript scriptCode;
|
|
CTransaction signTx(mtx);
|
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
|
|
|
|
// Add the signature
|
|
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
|
|
dataToBeSigned.begin(), 32,
|
|
joinSplitPrivKey
|
|
) == 0);
|
|
|
|
CTransaction tx {mtx};
|
|
CWalletTx wtx {NULL, tx};
|
|
return wtx;
|
|
}
|
|
|
|
libzcash::Note GetNote(ZCJoinSplit& params,
|
|
const libzcash::SpendingKey& sk,
|
|
const CTransaction& tx, size_t js, size_t n) {
|
|
ZCNoteDecryption decryptor {sk.receiving_key()};
|
|
auto hSig = tx.vjoinsplit[js].h_sig(params, tx.joinSplitPubKey);
|
|
auto note_pt = libzcash::NotePlaintext::decrypt(
|
|
decryptor,
|
|
tx.vjoinsplit[js].ciphertexts[n],
|
|
tx.vjoinsplit[js].ephemeralKey,
|
|
hSig,
|
|
(unsigned char) n);
|
|
return note_pt.note(sk.address());
|
|
}
|
|
|
|
CWalletTx GetValidSpend(ZCJoinSplit& params,
|
|
const libzcash::SpendingKey& sk,
|
|
const libzcash::Note& note, CAmount value) {
|
|
CMutableTransaction mtx;
|
|
mtx.vout.resize(2);
|
|
mtx.vout[0].nValue = value;
|
|
mtx.vout[1].nValue = 0;
|
|
|
|
// Generate an ephemeral keypair.
|
|
uint256 joinSplitPubKey;
|
|
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
|
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
|
|
mtx.joinSplitPubKey = joinSplitPubKey;
|
|
|
|
// Fake tree for the unused witness
|
|
ZCIncrementalMerkleTree tree;
|
|
|
|
libzcash::JSOutput dummyout;
|
|
libzcash::JSInput dummyin;
|
|
|
|
{
|
|
if (note.value > value) {
|
|
libzcash::SpendingKey dummykey = libzcash::SpendingKey::random();
|
|
libzcash::PaymentAddress dummyaddr = dummykey.address();
|
|
dummyout = libzcash::JSOutput(dummyaddr, note.value - value);
|
|
} else if (note.value < value) {
|
|
libzcash::SpendingKey dummykey = libzcash::SpendingKey::random();
|
|
libzcash::PaymentAddress dummyaddr = dummykey.address();
|
|
libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256());
|
|
tree.append(dummynote.cm());
|
|
dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey);
|
|
}
|
|
}
|
|
|
|
tree.append(note.cm());
|
|
|
|
boost::array<libzcash::JSInput, 2> inputs = {
|
|
libzcash::JSInput(tree.witness(), note, sk),
|
|
dummyin
|
|
};
|
|
|
|
boost::array<libzcash::JSOutput, 2> outputs = {
|
|
dummyout, // dummy output
|
|
libzcash::JSOutput() // dummy output
|
|
};
|
|
|
|
boost::array<libzcash::Note, 2> output_notes;
|
|
|
|
// Prepare JoinSplits
|
|
uint256 rt = tree.root();
|
|
JSDescription jsdesc {params, mtx.joinSplitPubKey, rt,
|
|
inputs, outputs, 0, value, false};
|
|
mtx.vjoinsplit.push_back(jsdesc);
|
|
|
|
// Empty output script.
|
|
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
|
CScript scriptCode;
|
|
CTransaction signTx(mtx);
|
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
|
|
|
|
// Add the signature
|
|
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
|
|
dataToBeSigned.begin(), 32,
|
|
joinSplitPrivKey
|
|
) == 0);
|
|
CTransaction tx {mtx};
|
|
CWalletTx wtx {NULL, tx};
|
|
return wtx;
|
|
}
|