aboutsummaryrefslogtreecommitdiff
path: root/src/script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script.cpp')
-rw-r--r--src/script.cpp220
1 files changed, 204 insertions, 16 deletions
diff --git a/src/script.cpp b/src/script.cpp
index e8dd93d081..36d208c247 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -253,7 +253,68 @@ const char* GetOpName(opcodetype opcode)
}
}
-bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
+bool IsCanonicalPubKey(const valtype &vchPubKey) {
+ if (vchPubKey.size() < 33)
+ return error("Non-canonical public key: too short");
+ if (vchPubKey[0] == 0x04) {
+ if (vchPubKey.size() != 65)
+ return error("Non-canonical public key: invalid length for uncompressed key");
+ } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
+ if (vchPubKey.size() != 33)
+ return error("Non-canonical public key: invalid length for compressed key");
+ } else {
+ return error("Non-canonical public key: compressed nor uncompressed");
+ }
+ return true;
+}
+
+bool IsCanonicalSignature(const valtype &vchSig) {
+ // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
+ // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
+ // Where R and S are not negative (their first byte has its highest bit not set), and not
+ // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
+ // in which case a single 0 byte is necessary and even required).
+ if (vchSig.size() < 9)
+ return error("Non-canonical signature: too short");
+ if (vchSig.size() > 73)
+ return error("Non-canonical signature: too long");
+ if (vchSig[vchSig.size() - 1] & 0x7C)
+ return error("Non-canonical signature: unknown hashtype byte");
+ if (vchSig[0] != 0x30)
+ return error("Non-canonical signature: wrong type");
+ if (vchSig[1] != vchSig.size()-3)
+ return error("Non-canonical signature: wrong length marker");
+ unsigned int nLenR = vchSig[3];
+ if (5 + nLenR >= vchSig.size())
+ return error("Non-canonical signature: S length misplaced");
+ unsigned int nLenS = vchSig[5+nLenR];
+ if ((unsigned long)(nLenR+nLenS+7) != vchSig.size())
+ return error("Non-canonical signature: R+S length mismatch");
+
+ const unsigned char *R = &vchSig[4];
+ if (R[-2] != 0x02)
+ return error("Non-canonical signature: R value type mismatch");
+ if (nLenR == 0)
+ return error("Non-canonical signature: R length is zero");
+ if (R[0] & 0x80)
+ return error("Non-canonical signature: R value negative");
+ if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80))
+ return error("Non-canonical signature: R value excessively padded");
+
+ const unsigned char *S = &vchSig[6+nLenR];
+ if (S[-2] != 0x02)
+ return error("Non-canonical signature: S value type mismatch");
+ if (nLenS == 0)
+ return error("Non-canonical signature: S length is zero");
+ if (S[0] & 0x80)
+ return error("Non-canonical signature: S value negative");
+ if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80))
+ return error("Non-canonical signature: S value excessively padded");
+
+ return true;
+}
+
+bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, bool fStrictEncodings, int nHashType)
{
CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin();
@@ -944,7 +1005,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));
- bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+ bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
+ if (fSuccess)
+ fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
popstack(stack);
popstack(stack);
@@ -1004,8 +1067,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
valtype& vchPubKey = stacktop(-ikey);
// Check signature
- if (CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType))
- {
+ bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
+ if (fOk)
+ fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+
+ if (fOk) {
isig++;
nSigsCount--;
}
@@ -1571,14 +1637,14 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
}
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
- bool fValidatePayToScriptHash, int nHashType)
+ bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType)
{
vector<vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptSig, txTo, nIn, fStrictEncodings, nHashType))
return false;
if (fValidatePayToScriptHash)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptPubKey, txTo, nIn, fStrictEncodings, nHashType))
return false;
if (stack.empty())
return false;
@@ -1596,7 +1662,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stackCopy);
- if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType))
+ if (!EvalScript(stackCopy, pubKey2, txTo, nIn, fStrictEncodings, nHashType))
return false;
if (stackCopy.empty())
return false;
@@ -1639,7 +1705,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa
}
// Test solution
- return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, 0);
+ return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, true, true, 0);
}
bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType)
@@ -1652,7 +1718,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType);
}
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType)
+bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType)
{
assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn];
@@ -1660,10 +1726,7 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
return false;
const CTxOut& txout = txFrom.vout[txin.prevout.n];
- if (txin.prevout.hash != txFrom.GetHash())
- return false;
-
- return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType);
+ return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType);
}
static CScript PushAll(const vector<valtype>& values)
@@ -1781,9 +1844,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign
Solver(scriptPubKey, txType, vSolutions);
vector<valtype> stack1;
- EvalScript(stack1, scriptSig1, CTransaction(), 0, 0);
+ EvalScript(stack1, scriptSig1, CTransaction(), 0, true, 0);
vector<valtype> stack2;
- EvalScript(stack2, scriptSig2, CTransaction(), 0, 0);
+ EvalScript(stack2, scriptSig2, CTransaction(), 0, true, 0);
return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2);
}
@@ -1884,3 +1947,128 @@ void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
*this << key.GetPubKey();
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
}
+
+bool CScriptCompressor::IsToKeyID(CKeyID &hash) const
+{
+ if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160
+ && script[2] == 20 && script[23] == OP_EQUALVERIFY
+ && script[24] == OP_CHECKSIG) {
+ memcpy(&hash, &script[3], 20);
+ return true;
+ }
+ return false;
+}
+
+bool CScriptCompressor::IsToScriptID(CScriptID &hash) const
+{
+ if (script.size() == 23 && script[0] == OP_HASH160 && script[1] == 20
+ && script[22] == OP_EQUAL) {
+ memcpy(&hash, &script[2], 20);
+ return true;
+ }
+ return false;
+}
+
+bool CScriptCompressor::IsToPubKey(std::vector<unsigned char> &pubkey) const
+{
+ if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG
+ && (script[1] == 0x02 || script[1] == 0x03)) {
+ pubkey.resize(33);
+ memcpy(&pubkey[0], &script[1], 33);
+ return true;
+ }
+ if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG
+ && script[1] == 0x04) {
+ pubkey.resize(65);
+ memcpy(&pubkey[0], &script[1], 65);
+ CKey key;
+ return (key.SetPubKey(CPubKey(pubkey))); // SetPubKey fails if this is not a valid public key, a case that would not be compressible
+ }
+ return false;
+}
+
+bool CScriptCompressor::Compress(std::vector<unsigned char> &out) const
+{
+ CKeyID keyID;
+ if (IsToKeyID(keyID)) {
+ out.resize(21);
+ out[0] = 0x00;
+ memcpy(&out[1], &keyID, 20);
+ return true;
+ }
+ CScriptID scriptID;
+ if (IsToScriptID(scriptID)) {
+ out.resize(21);
+ out[0] = 0x01;
+ memcpy(&out[1], &scriptID, 20);
+ return true;
+ }
+ std::vector<unsigned char> pubkey;
+ if (IsToPubKey(pubkey)) {
+ out.resize(33);
+ memcpy(&out[1], &pubkey[1], 32);
+ if (pubkey[0] == 0x02 || pubkey[0] == 0x03) {
+ out[0] = pubkey[0];
+ return true;
+ } else if (pubkey[0] == 0x04) {
+ out[0] = 0x04 | (pubkey[64] & 0x01);
+ return true;
+ }
+ }
+ return false;
+}
+
+unsigned int CScriptCompressor::GetSpecialSize(unsigned int nSize) const
+{
+ if (nSize == 0 || nSize == 1)
+ return 20;
+ if (nSize == 2 || nSize == 3 || nSize == 4 || nSize == 5)
+ return 32;
+ return 0;
+}
+
+bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigned char> &in)
+{
+ switch(nSize) {
+ case 0x00:
+ script.resize(25);
+ script[0] = OP_DUP;
+ script[1] = OP_HASH160;
+ script[2] = 20;
+ memcpy(&script[3], &in[0], 20);
+ script[23] = OP_EQUALVERIFY;
+ script[24] = OP_CHECKSIG;
+ return true;
+ case 0x01:
+ script.resize(23);
+ script[0] = OP_HASH160;
+ script[1] = 20;
+ memcpy(&script[2], &in[0], 20);
+ script[22] = OP_EQUAL;
+ return true;
+ case 0x02:
+ case 0x03:
+ script.resize(35);
+ script[0] = 33;
+ script[1] = nSize;
+ memcpy(&script[2], &in[0], 32);
+ script[34] = OP_CHECKSIG;
+ return true;
+ case 0x04:
+ case 0x05:
+ std::vector<unsigned char> vch(33, 0x00);
+ vch[0] = nSize - 2;
+ memcpy(&vch[1], &in[0], 32);
+ CKey key;
+ if (!key.SetPubKey(CPubKey(vch)))
+ return false;
+ key.SetCompressedPubKey(false); // Decompress public key
+ CPubKey pubkey = key.GetPubKey();
+ script.resize(67);
+ script[0] = 65;
+ memcpy(&script[1], &pubkey.Raw()[0], 65);
+ script[66] = OP_CHECKSIG;
+ return true;
+ }
+ return false;
+}