Auto merge of #3233 - str4d:3164-sapling-sighash, r=str4d
ZIP 243: Sapling SignatureHash Closes #3164.
This commit is contained in:
commit
f0daf3915f
|
@ -1065,6 +1065,10 @@ const unsigned char ZCASH_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2
|
||||||
{'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'};
|
{'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'};
|
||||||
const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
const unsigned char ZCASH_JOINSPLITS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||||
{'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'};
|
{'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'};
|
||||||
|
const unsigned char ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||||
|
{'Z','c','a','s','h','S','S','p','e','n','d','s','H','a','s','h'};
|
||||||
|
const unsigned char ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||||
|
{'Z','c','a','s','h','S','O','u','t','p','u','t','H','a','s','h'};
|
||||||
|
|
||||||
uint256 GetPrevoutHash(const CTransaction& txTo) {
|
uint256 GetPrevoutHash(const CTransaction& txTo) {
|
||||||
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION);
|
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_PREVOUTS_HASH_PERSONALIZATION);
|
||||||
|
@ -1099,6 +1103,26 @@ uint256 GetJoinSplitsHash(const CTransaction& txTo) {
|
||||||
return ss.GetHash();
|
return ss.GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint256 GetShieldedSpendsHash(const CTransaction& txTo) {
|
||||||
|
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION);
|
||||||
|
for (unsigned int n = 0; n < txTo.vShieldedSpend.size(); n++) {
|
||||||
|
ss << txTo.vShieldedSpend[n].cv;
|
||||||
|
ss << txTo.vShieldedSpend[n].anchor;
|
||||||
|
ss << txTo.vShieldedSpend[n].nullifier;
|
||||||
|
ss << txTo.vShieldedSpend[n].rk;
|
||||||
|
ss << txTo.vShieldedSpend[n].zkproof;
|
||||||
|
}
|
||||||
|
return ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 GetShieldedOutputsHash(const CTransaction& txTo) {
|
||||||
|
CBLAKE2bWriter ss(SER_GETHASH, 0, ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION);
|
||||||
|
for (unsigned int n = 0; n < txTo.vShieldedOutput.size(); n++) {
|
||||||
|
ss << txTo.vShieldedOutput[n];
|
||||||
|
}
|
||||||
|
return ss.GetHash();
|
||||||
|
}
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
|
||||||
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||||
|
@ -1107,12 +1131,18 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
|
||||||
hashSequence = GetSequenceHash(txTo);
|
hashSequence = GetSequenceHash(txTo);
|
||||||
hashOutputs = GetOutputsHash(txTo);
|
hashOutputs = GetOutputsHash(txTo);
|
||||||
hashJoinSplits = GetJoinSplitsHash(txTo);
|
hashJoinSplits = GetJoinSplitsHash(txTo);
|
||||||
|
hashShieldedSpends = GetShieldedSpendsHash(txTo);
|
||||||
|
hashShieldedOutputs = GetShieldedOutputsHash(txTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
SigVersion SignatureHashVersion(const CTransaction& txTo)
|
SigVersion SignatureHashVersion(const CTransaction& txTo)
|
||||||
{
|
{
|
||||||
if (txTo.fOverwintered) {
|
if (txTo.fOverwintered) {
|
||||||
return SIGVERSION_OVERWINTER;
|
if (txTo.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
||||||
|
return SIGVERSION_SAPLING;
|
||||||
|
} else {
|
||||||
|
return SIGVERSION_OVERWINTER;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return SIGVERSION_SPROUT;
|
return SIGVERSION_SPROUT;
|
||||||
}
|
}
|
||||||
|
@ -1134,11 +1164,13 @@ uint256 SignatureHash(
|
||||||
|
|
||||||
auto sigversion = SignatureHashVersion(txTo);
|
auto sigversion = SignatureHashVersion(txTo);
|
||||||
|
|
||||||
if (sigversion == SIGVERSION_OVERWINTER) {
|
if (sigversion == SIGVERSION_OVERWINTER || sigversion == SIGVERSION_SAPLING) {
|
||||||
uint256 hashPrevouts;
|
uint256 hashPrevouts;
|
||||||
uint256 hashSequence;
|
uint256 hashSequence;
|
||||||
uint256 hashOutputs;
|
uint256 hashOutputs;
|
||||||
uint256 hashJoinSplits;
|
uint256 hashJoinSplits;
|
||||||
|
uint256 hashShieldedSpends;
|
||||||
|
uint256 hashShieldedOutputs;
|
||||||
|
|
||||||
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
|
||||||
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
|
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
|
||||||
|
@ -1160,6 +1192,14 @@ uint256 SignatureHash(
|
||||||
hashJoinSplits = cache ? cache->hashJoinSplits : GetJoinSplitsHash(txTo);
|
hashJoinSplits = cache ? cache->hashJoinSplits : GetJoinSplitsHash(txTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!txTo.vShieldedSpend.empty()) {
|
||||||
|
hashShieldedSpends = cache ? cache->hashShieldedSpends : GetShieldedSpendsHash(txTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!txTo.vShieldedOutput.empty()) {
|
||||||
|
hashShieldedOutputs = cache ? cache->hashShieldedOutputs : GetShieldedOutputsHash(txTo);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t leConsensusBranchId = htole32(consensusBranchId);
|
uint32_t leConsensusBranchId = htole32(consensusBranchId);
|
||||||
unsigned char personalization[16] = {};
|
unsigned char personalization[16] = {};
|
||||||
memcpy(personalization, "ZcashSigHash", 12);
|
memcpy(personalization, "ZcashSigHash", 12);
|
||||||
|
@ -1177,10 +1217,20 @@ uint256 SignatureHash(
|
||||||
ss << hashOutputs;
|
ss << hashOutputs;
|
||||||
// JoinSplits
|
// JoinSplits
|
||||||
ss << hashJoinSplits;
|
ss << hashJoinSplits;
|
||||||
|
if (sigversion == SIGVERSION_SAPLING) {
|
||||||
|
// Spend descriptions
|
||||||
|
ss << hashShieldedSpends;
|
||||||
|
// Output descriptions
|
||||||
|
ss << hashShieldedOutputs;
|
||||||
|
}
|
||||||
// Locktime
|
// Locktime
|
||||||
ss << txTo.nLockTime;
|
ss << txTo.nLockTime;
|
||||||
// Expiry height
|
// Expiry height
|
||||||
ss << txTo.nExpiryHeight;
|
ss << txTo.nExpiryHeight;
|
||||||
|
if (sigversion == SIGVERSION_SAPLING) {
|
||||||
|
// Sapling value balance
|
||||||
|
ss << txTo.valueBalance;
|
||||||
|
}
|
||||||
// Sighash type
|
// Sighash type
|
||||||
ss << nHashType;
|
ss << nHashType;
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
|
||||||
|
|
||||||
struct PrecomputedTransactionData
|
struct PrecomputedTransactionData
|
||||||
{
|
{
|
||||||
uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits;
|
uint256 hashPrevouts, hashSequence, hashOutputs, hashJoinSplits, hashShieldedSpends, hashShieldedOutputs;
|
||||||
|
|
||||||
PrecomputedTransactionData(const CTransaction& tx);
|
PrecomputedTransactionData(const CTransaction& tx);
|
||||||
};
|
};
|
||||||
|
@ -101,6 +101,7 @@ enum SigVersion
|
||||||
{
|
{
|
||||||
SIGVERSION_SPROUT = 0,
|
SIGVERSION_SPROUT = 0,
|
||||||
SIGVERSION_OVERWINTER = 1,
|
SIGVERSION_OVERWINTER = 1,
|
||||||
|
SIGVERSION_SAPLING = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint256 SignatureHash(
|
uint256 SignatureHash(
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -99,23 +99,37 @@ void static RandomScript(CScript &script) {
|
||||||
// https://stackoverflow.com/a/19728404
|
// https://stackoverflow.com/a/19728404
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 rng(rd());
|
std::mt19937 rng(rd());
|
||||||
std::uniform_int_distribution<int> version_dist(CTransaction::OVERWINTER_MIN_CURRENT_VERSION,
|
std::uniform_int_distribution<int> overwinter_version_dist(
|
||||||
CTransaction::OVERWINTER_MAX_CURRENT_VERSION);
|
CTransaction::OVERWINTER_MIN_CURRENT_VERSION,
|
||||||
|
CTransaction::OVERWINTER_MAX_CURRENT_VERSION);
|
||||||
|
std::uniform_int_distribution<int> sapling_version_dist(
|
||||||
|
CTransaction::SAPLING_MIN_CURRENT_VERSION,
|
||||||
|
CTransaction::SAPLING_MAX_CURRENT_VERSION);
|
||||||
|
|
||||||
void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t consensusBranchId) {
|
void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t consensusBranchId) {
|
||||||
tx.fOverwintered = insecure_rand() % 2;
|
tx.fOverwintered = insecure_rand() % 2;
|
||||||
if (tx.fOverwintered) {
|
if (tx.fOverwintered) {
|
||||||
tx.nVersion = version_dist(rng);
|
if (insecure_rand() % 2) {
|
||||||
tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
tx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
|
||||||
|
tx.nVersion = sapling_version_dist(rng);
|
||||||
|
} else {
|
||||||
|
tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
||||||
|
tx.nVersion = overwinter_version_dist(rng);
|
||||||
|
}
|
||||||
tx.nExpiryHeight = (insecure_rand() % 2) ? insecure_rand() : 0;
|
tx.nExpiryHeight = (insecure_rand() % 2) ? insecure_rand() : 0;
|
||||||
} else {
|
} else {
|
||||||
tx.nVersion = insecure_rand() & 0x7FFFFFFF;
|
tx.nVersion = insecure_rand() & 0x7FFFFFFF;
|
||||||
}
|
}
|
||||||
tx.vin.clear();
|
tx.vin.clear();
|
||||||
tx.vout.clear();
|
tx.vout.clear();
|
||||||
|
tx.vShieldedSpend.clear();
|
||||||
|
tx.vShieldedOutput.clear();
|
||||||
|
tx.vjoinsplit.clear();
|
||||||
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
|
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
|
||||||
int ins = (insecure_rand() % 4) + 1;
|
int ins = (insecure_rand() % 4) + 1;
|
||||||
int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
|
int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
|
||||||
|
int shielded_spends = (insecure_rand() % 4) + 1;
|
||||||
|
int shielded_outs = (insecure_rand() % 4) + 1;
|
||||||
int joinsplits = (insecure_rand() % 4);
|
int joinsplits = (insecure_rand() % 4);
|
||||||
for (int in = 0; in < ins; in++) {
|
for (int in = 0; in < ins; in++) {
|
||||||
tx.vin.push_back(CTxIn());
|
tx.vin.push_back(CTxIn());
|
||||||
|
@ -131,6 +145,28 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co
|
||||||
txout.nValue = insecure_rand() % 100000000;
|
txout.nValue = insecure_rand() % 100000000;
|
||||||
RandomScript(txout.scriptPubKey);
|
RandomScript(txout.scriptPubKey);
|
||||||
}
|
}
|
||||||
|
if (tx.nVersionGroupId == SAPLING_VERSION_GROUP_ID) {
|
||||||
|
tx.valueBalance = insecure_rand() % 100000000;
|
||||||
|
for (int spend = 0; spend < shielded_spends; spend++) {
|
||||||
|
SpendDescription sdesc;
|
||||||
|
sdesc.cv = GetRandHash();
|
||||||
|
sdesc.anchor = GetRandHash();
|
||||||
|
sdesc.nullifier = GetRandHash();
|
||||||
|
sdesc.rk = GetRandHash();
|
||||||
|
randombytes_buf(sdesc.zkproof.begin(), sdesc.zkproof.size());
|
||||||
|
tx.vShieldedSpend.push_back(sdesc);
|
||||||
|
}
|
||||||
|
for (int out = 0; out < shielded_outs; out++) {
|
||||||
|
OutputDescription odesc;
|
||||||
|
odesc.cv = GetRandHash();
|
||||||
|
odesc.cm = GetRandHash();
|
||||||
|
odesc.ephemeralKey = GetRandHash();
|
||||||
|
randombytes_buf(odesc.encCiphertext.begin(), odesc.encCiphertext.size());
|
||||||
|
randombytes_buf(odesc.outCiphertext.begin(), odesc.outCiphertext.size());
|
||||||
|
randombytes_buf(odesc.zkproof.begin(), odesc.zkproof.size());
|
||||||
|
tx.vShieldedOutput.push_back(odesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (tx.nVersion >= 2) {
|
if (tx.nVersion >= 2) {
|
||||||
for (int js = 0; js < joinsplits; js++) {
|
for (int js = 0; js < joinsplits; js++) {
|
||||||
JSDescription jsdesc;
|
JSDescription jsdesc;
|
||||||
|
@ -147,7 +183,13 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t co
|
||||||
jsdesc.randomSeed = GetRandHash();
|
jsdesc.randomSeed = GetRandHash();
|
||||||
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
||||||
randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size());
|
randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size());
|
||||||
jsdesc.proof = libzcash::ZCProof::random_invalid();
|
if (tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION) {
|
||||||
|
libzcash::GrothProof zkproof;
|
||||||
|
randombytes_buf(zkproof.begin(), zkproof.size());
|
||||||
|
jsdesc.proof = zkproof;
|
||||||
|
} else {
|
||||||
|
jsdesc.proof = libzcash::ZCProof::random_invalid();
|
||||||
|
}
|
||||||
jsdesc.macs[0] = GetRandHash();
|
jsdesc.macs[0] = GetRandHash();
|
||||||
jsdesc.macs[1] = GetRandHash();
|
jsdesc.macs[1] = GetRandHash();
|
||||||
|
|
||||||
|
@ -187,7 +229,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
|
||||||
#endif
|
#endif
|
||||||
for (int i=0; i<nRandomTests; i++) {
|
for (int i=0; i<nRandomTests; i++) {
|
||||||
int nHashType = insecure_rand();
|
int nHashType = insecure_rand();
|
||||||
uint32_t consensusBranchId = insecure_rand() % 2 ? SPROUT_BRANCH_ID : overwinterBranchId;
|
uint32_t consensusBranchId = NetworkUpgradeInfo[insecure_rand() % Consensus::MAX_NETWORK_UPGRADES].nBranchId;
|
||||||
CMutableTransaction txTo;
|
CMutableTransaction txTo;
|
||||||
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE, consensusBranchId);
|
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE, consensusBranchId);
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
|
|
Loading…
Reference in New Issue