Skip to content

Commit

Permalink
initial implementation of BUIP-HF Digest for replay protection across…
Browse files Browse the repository at this point in the history
… forks
  • Loading branch information
wisp3r committed May 8, 2018
1 parent 8b9b6a4 commit b0cf334
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 20 deletions.
26 changes: 16 additions & 10 deletions src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,8 @@ Value signrawtransaction(const Array& params, bool fHelp)
"this transaction depends on but may not yet be in the blockchain.\n"
"Third optional argument (may be null) is an array of base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the transaction.\n"
"Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
"ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
"Fourth optional argument is a string beginning with one of three values; ALL, NONE, SINGLE, optionally\n"
"combined using | with one or both of FORKID and ANYONECANPAY.\n"
"Returns json object with keys:\n"
" hex : raw transaction with signature(s) (hex-encoded string)\n"
" complete : 1 if transaction has a complete set of signature (0 if not)"
Expand Down Expand Up @@ -497,17 +497,23 @@ Value signrawtransaction(const Array& params, bool fHelp)

const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);

int nHashType = SIGHASH_ALL;
int nHashType = SIGHASH_ALL|SIGHASH_FORKID;
if (params.size() > 3 && params[3].type() != null_type)
{
static map<string, int> mapSigHashValues =
boost::assign::map_list_of
(string("ALL"), int(SIGHASH_ALL))
(string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
(string("NONE"), int(SIGHASH_NONE))
(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
(string("SINGLE"), int(SIGHASH_SINGLE))
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
(string("ALL"), int(SIGHASH_ALL))
(string("ALL|FORKID"), int(SIGHASH_ALL|SIGHASH_FORKID))
(string("ALL|FORKID|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_FORKID|SIGHASH_ANYONECANPAY))
(string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
(string("NONE"), int(SIGHASH_NONE))
(string("NONE|FORKID"), int(SIGHASH_NONE|SIGHASH_FORKID))
(string("NONE|FORKID|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_FORKID|SIGHASH_ANYONECANPAY))
(string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
(string("SINGLE"), int(SIGHASH_SINGLE))
(string("SINGLE|FORKID"), int(SIGHASH_SINGLE|SIGHASH_FORKID))
(string("SINGLE|FORKID|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_FORKID|SIGHASH_ANYONECANPAY))
(string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
;
string strHashType = params[3].get_str();
if (mapSigHashValues.count(strHashType))
Expand All @@ -516,7 +522,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
};

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

// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
Expand Down
93 changes: 87 additions & 6 deletions src/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
if (vchSig.size() == 0) {
return false;
}
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY));
unsigned char nHashType = vchSig[vchSig.size() - 1] & (~(SIGHASH_ANYONECANPAY | SIGHASH_FORKID));
if (nHashType < SIGHASH_ALL || nHashType > SIGHASH_SINGLE)
return error("Non-canonical signature: unknown hashtype byte");

Expand All @@ -345,14 +345,26 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags) {
// Empty signature. Not strictly DER encoded, but allowed to provide a
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG
if ((flags & SCRIPT_VERIFY_ALLOW_EMPTY_SIG) && vchSig.size() == 0) {
if ((flags & SCRIPT_VERIFY_ALLOW_EMPTY_SIG) && vchSig.size() == 0)
return true;
}
if (!IsLowDERSignature(vchSig)) {
if (!IsLowDERSignature(vchSig))
return false;
} else if (!(flags & SCRIPT_VERIFY_FIX_HASHTYPE) && !IsDefinedHashtypeSignature(vchSig)) {
else if (!(flags & SCRIPT_VERIFY_FIX_HASHTYPE) && !IsDefinedHashtypeSignature(vchSig))
return false;

if ((flags & SCRIPT_VERIFY_STRICTENC) != 0)
{
if (!IsDefinedHashtypeSignature(vchSig))
return false;

unsigned char nHashType = vchSig[vchSig.size() - 1];
bool wispForkHash = nHashType & SIGHASH_FORKID;
bool wispForkEnabled = flags & SCRIPT_ENABLE_SIGHASH_FORKID;

if ((!wispForkHash && wispForkEnabled) || (wispForkHash && !wispForkEnabled))
return false;
}

return true;
}

Expand Down Expand Up @@ -1267,10 +1279,36 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}


uint256 GetPrevoutHash(const CTransaction &txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vin.size(); ++n)
ss << txTo.vin[n].prevout;
return ss.GetHash();
}


uint256 GetSequenceHash(const CTransaction &txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vin.size(); ++n)
ss << txTo.vin[n].nSequence;
return ss.GetHash();
}


uint256 GetOutputsHash(const CTransaction &txTo)
{
CHashWriter ss(SER_GETHASH, 0);
for (unsigned int n = 0; n < txTo.vout.size(); ++n)
ss << txTo.vout[n];
return ss.GetHash();
}


uint256 GetForkId() {
return 41; // arbitrary fork ID of WISP
}


uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
Expand Down Expand Up @@ -1330,7 +1368,47 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int

// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;

if (nHashType & SIGHASH_FORKID)
{
// BUIP-HF Digest for replay protection across forks.
// https://github.com/bitcoincashorg/spec/blob/master/replay-protected-sighash.md
uint256 hashPrevOuts;
uint256 hashSeq;
uint256 hashOuts;

if (!(nHashType & SIGHASH_ANYONECANPAY))
hashPrevOuts = GetPrevoutHash(txTo);

if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE)
hashSeq = GetSequenceHash(txTo);

if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE)
hashOuts = GetOutputsHash(txTo);
else
{
CHashWriter oss(SER_GETHASH, 0);
oss << txTo.vout[nIn];
hashOuts = oss.GetHash();
}

ss << txTo.nVersion; // Version
ss << hashPrevOuts; // Input prevouts
ss << hashSeq; // Input sequence
ss << txTo.vin[nIn].prevout;
ss << static_cast<const CScriptBase &>(scriptCode);
ss << amount;
ss << txTo.vin[nIn].nSequence;
ss << hashOuts;
ss << txTo.nLockTime;
ss << ((GetForkId() << 8) | nHashType);
}
else
{
CHashWriter ss(SER_GETHASH, 0);
ss << txTmp << nHashType;
}

return ss.GetHash();
}

Expand Down Expand Up @@ -1781,6 +1859,9 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
unsigned int flags, int nHashType)
{
if (flags & SCRIPT_ENABLE_SIGHASH_FORKID)
flags |= SCRIPT_VERIFY_STRICTENC;

vector<vector<unsigned char> > stack, stackCopy;
if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType))
return false;
Expand Down
10 changes: 7 additions & 3 deletions src/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum
SIGHASH_NONE = 2,
SIGHASH_SINGLE = 3,
SIGHASH_ANYONECANPAY = 0x80,
SIGHASH_FORKID = 0x42,
};

/** Script verification flags */
Expand Down Expand Up @@ -71,6 +72,8 @@ enum
// Verify CHECKLOCKTIMEVERIFY (BIP65)
//
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 7),

SCRIPT_ENABLE_SIGHASH_FORKID = (1U << 10),
};

// Mandatory script verification flags that all new blocks must comply with for
Expand All @@ -81,7 +84,8 @@ enum
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH |
SCRIPT_VERIFY_NULLDUMMY |
SCRIPT_VERIFY_STRICTENC |
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_ENABLE_SIGHASH_FORKID;

// Standard script verification flags that standard transactions will comply
// with. However scripts violating these flags may still be present in valid
Expand Down Expand Up @@ -737,8 +741,8 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL|SIGHASH_FORKID);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL|SIGHASH_FORKID);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
unsigned int flags, int nHashType);
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
Expand Down
2 changes: 1 addition & 1 deletion src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs)

// A couple of partially-signed versions:
vector<unsigned char> sig1;
uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL);
uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL | SIGHASH_FORKID);
BOOST_CHECK(keys[0].Sign(hash1, sig1));
sig1.push_back(SIGHASH_ALL);
vector<unsigned char> sig2;
Expand Down

0 comments on commit b0cf334

Please sign in to comment.