Skip to content

Commit

Permalink
Auto merge of #2903 - str4d:1408-sighash, r=str4d
Browse files Browse the repository at this point in the history
Overwinter SignatureHash

Implements ZIP 143.

Includes code cherry-picked from the following upstream PRs:

- bitcoin/bitcoin#7276
- bitcoin/bitcoin#7976
- bitcoin/bitcoin#8118
- bitcoin/bitcoin#8149
  - Only amount validation and SignatureHash commits.
- bitcoin/bitcoin#8346
- bitcoin/bitcoin#8524

Part of #2074 and #2254. Closes #1408 and #2584.
  • Loading branch information
zkbot committed Feb 20, 2018
2 parents 4927455 + 4553901 commit 8487be8
Show file tree
Hide file tree
Showing 36 changed files with 1,495 additions and 875 deletions.
2 changes: 1 addition & 1 deletion qa/zcash/performance-measurements.sh
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ case "$1" in
zcash_rpc zcbenchmark verifyequihash 1000
;;
validatelargetx)
zcash_rpc zcbenchmark validatelargetx 5
zcash_rpc zcbenchmark validatelargetx 10 "${@:3}"
;;
trydecryptnotes)
zcash_rpc zcbenchmark trydecryptnotes 1000 "${@:3}"
Expand Down
58 changes: 48 additions & 10 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "clientversion.h"
#include "coins.h"
#include "consensus/consensus.h"
#include "consensus/upgrades.h"
#include "core_io.h"
#include "keystore.h"
#include "primitives/transaction.h"
Expand Down Expand Up @@ -70,7 +71,7 @@ static bool AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
strUsage += HelpMessageOpt("sign=HEIGHT:SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
_("This command requires JSON registers:") +
_("prevtxs=JSON object") + ", " +
_("privatekeys=JSON object") + ". " +
Expand Down Expand Up @@ -322,10 +323,39 @@ vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
return ParseHexUV(o[strKey], strKey);
}

static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
static CAmount AmountFromValue(const UniValue& value)
{
int nHashType = SIGHASH_ALL;
if (!value.isNum() && !value.isStr())
throw runtime_error("Amount is not a number or string");
CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
throw runtime_error("Invalid amount");
if (!MoneyRange(amount))
throw runtime_error("Amount out of range");
return amount;
}

static void MutateTxSign(CMutableTransaction& tx, const string& strInput)
{
// separate HEIGHT:SIGHASH-FLAGS in string
size_t pos = strInput.find(':');
if ((pos == 0) ||
(pos == (strInput.size() - 1)))
throw runtime_error("Invalid sighash flag separator");

// extract and validate HEIGHT
string strHeight = strInput.substr(0, pos);
int nHeight = atoi(strHeight);
if (nHeight <= 0) {
throw runtime_error("invalid height");
}

// extract and validate SIGHASH-FLAGS
int nHashType = SIGHASH_ALL;
string flagStr;
if (pos != string::npos) {
flagStr = strInput.substr(pos + 1, string::npos);
}
if (flagStr.size() > 0)
if (!findSighashFlags(nHashType, flagStr))
throw runtime_error("unknown sighash flag/sign option");
Expand Down Expand Up @@ -393,7 +423,10 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0; // we don't know the actual output value
coins->vout[nOut].nValue = 0;
if (prevOut.exists("amount")) {
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
}
}

// if redeemScript given and private keys given,
Expand All @@ -412,6 +445,9 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)

bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);

// Grab the consensus branch ID for the given height
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());

// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
Expand All @@ -421,17 +457,19 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
continue;
}
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
const CAmount& amount = coins->vout[txin.prevout.n].nValue;

txin.scriptSig.clear();
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId);

// ... and merge in other signatures:
BOOST_FOREACH(const CTransaction& txv, txVariants) {
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i)))
BOOST_FOREACH(const CTransaction& txv, txVariants)
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId);
UpdateTransaction(mergedTx, i, sigdata);

if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount), consensusBranchId))
fComplete = false;
}

Expand Down
14 changes: 10 additions & 4 deletions src/gtest/test_checktransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class MockCValidationState : public CValidationState {


CMutableTransaction GetValidTransaction() {
uint32_t consensusBranchId = SPROUT_BRANCH_ID;

CMutableTransaction mtx;
mtx.vin.resize(2);
mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
Expand Down Expand Up @@ -74,7 +76,7 @@ CMutableTransaction GetValidTransaction() {
// Empty output script.
CScript scriptCode;
CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
if (dataToBeSigned == one) {
throw std::runtime_error("SignatureHash failed");
}
Expand Down Expand Up @@ -352,23 +354,27 @@ TEST(checktransaction_tests, bad_txns_prevout_null) {
}

TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
SelectParams(CBaseChainParams::REGTEST);

CMutableTransaction mtx = GetValidTransaction();
mtx.joinSplitSig[0] += 1;
CTransaction tx(mtx);

MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
ContextualCheckTransaction(tx, state, 0, 100);
}

TEST(checktransaction_tests, non_canonical_ed25519_signature) {
SelectParams(CBaseChainParams::REGTEST);

CMutableTransaction mtx = GetValidTransaction();

// Check that the signature is valid before we add L
{
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100));
}

// Copied from libsodium/crypto_sign/ed25519/ref10/open.c
Expand All @@ -389,7 +395,7 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) {

MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
ContextualCheckTransaction(tx, state, 0, 100);
}

TEST(checktransaction_tests, OverwinterConstructors) {
Expand Down
5 changes: 4 additions & 1 deletion src/gtest/test_validation.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <gtest/gtest.h>

#include "consensus/upgrades.h"
#include "consensus/validation.h"
#include "main.h"
#include "utiltest.h"
Expand Down Expand Up @@ -70,8 +71,10 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
FakeCoinsViewDB fakeDB;
CCoinsViewCache view(&fakeDB);

auto consensusBranchId = SPROUT_BRANCH_ID;
CValidationState state;
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, Params(CBaseChainParams::MAIN).GetConsensus()));
PrecomputedTransactionData txdata(tx);
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
}

TEST(Validation, ReceivedBlockTransactions) {
Expand Down
43 changes: 43 additions & 0 deletions src/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "uint256.h"
#include "version.h"

#include "sodium.h"

#include <vector>

typedef uint256 ChainCode;
Expand Down Expand Up @@ -150,6 +152,47 @@ class CHashWriter
}
};


/** A writer stream (for serialization) that computes a 256-bit BLAKE2b hash. */
class CBLAKE2bWriter
{
private:
crypto_generichash_blake2b_state state;

public:
int nType;
int nVersion;

CBLAKE2bWriter(int nTypeIn, int nVersionIn, const unsigned char* personal) : nType(nTypeIn), nVersion(nVersionIn) {
assert(crypto_generichash_blake2b_init_salt_personal(
&state,
NULL, 0, // No key.
32,
NULL, // No salt.
personal) == 0);
}

CBLAKE2bWriter& write(const char *pch, size_t size) {
crypto_generichash_blake2b_update(&state, (const unsigned char*)pch, size);
return (*this);
}

// invalidates the object
uint256 GetHash() {
uint256 result;
crypto_generichash_blake2b_final(&state, (unsigned char*)&result, 32);
return result;
}

template<typename T>
CBLAKE2bWriter& operator<<(const T& obj) {
// Serialize to this stream
::Serialize(*this, obj, nType, nVersion);
return (*this);
}
};


/** Compute the 256-bit hash of an object's serialization. */
template<typename T>
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Expand Down
Loading

1 comment on commit 8487be8

@zptec
Copy link

@zptec zptec commented on 8487be8 Feb 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your hard work dear zkbot.I want To know when can the new version 1.0.15 been released?

Please sign in to comment.