aboutsummaryrefslogtreecommitdiff
path: root/src/script
diff options
context:
space:
mode:
Diffstat (limited to 'src/script')
-rw-r--r--src/script/interpreter.cpp63
-rw-r--r--src/script/interpreter.h29
-rw-r--r--src/script/ismine.cpp100
-rw-r--r--src/script/ismine.h14
-rw-r--r--src/script/script.cpp5
-rw-r--r--src/script/script.h7
-rw-r--r--src/script/script_error.cpp4
-rw-r--r--src/script/script_error.h4
-rw-r--r--src/script/sign.cpp408
-rw-r--r--src/script/sign.h45
-rw-r--r--src/script/standard.cpp160
-rw-r--r--src/script/standard.h1
12 files changed, 424 insertions, 416 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index e0d193fa38..4b982d647d 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -336,6 +336,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
opcode == OP_RSHIFT)
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
+ // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch
+ if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR);
+
if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) {
if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) {
return set_error(serror, SCRIPT_ERR_MINIMALDATA);
@@ -899,6 +903,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
case OP_CODESEPARATOR:
{
+ // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit
+ // script, even in an unexecuted branch (this is checked above the opcode case statement).
+
// Hash starts after the code separator
pbegincodehash = pc;
}
@@ -919,7 +926,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
// Drop the signature in pre-segwit scripts but not segwit scripts
if (sigversion == SigVersion::BASE) {
- FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript(vchSig));
+ if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
@@ -983,7 +992,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
{
valtype& vchSig = stacktop(-isig-k);
if (sigversion == SigVersion::BASE) {
- FindAndDelete(scriptCode, CScript(vchSig));
+ int found = FindAndDelete(scriptCode, CScript(vchSig));
+ if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
+ return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}
}
@@ -1078,9 +1089,11 @@ namespace {
* Wrapper that serializes like CTransaction, but with the modifications
* required for the signature hash done in-place
*/
-class CTransactionSignatureSerializer {
+template <class T>
+class CTransactionSignatureSerializer
+{
private:
- const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized)
+ const T& txTo; //!< reference to the spending transaction (the one being serialized)
const CScript& scriptCode; //!< output script being consumed
const unsigned int nIn; //!< input index of txTo being signed
const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set
@@ -1088,7 +1101,7 @@ private:
const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE
public:
- CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
+ CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) :
txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn),
fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)),
fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE),
@@ -1169,7 +1182,9 @@ public:
}
};
-uint256 GetPrevoutHash(const CTransaction& txTo) {
+template <class T>
+uint256 GetPrevoutHash(const T& txTo)
+{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.prevout;
@@ -1177,7 +1192,9 @@ uint256 GetPrevoutHash(const CTransaction& txTo) {
return ss.GetHash();
}
-uint256 GetSequenceHash(const CTransaction& txTo) {
+template <class T>
+uint256 GetSequenceHash(const T& txTo)
+{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txin : txTo.vin) {
ss << txin.nSequence;
@@ -1185,7 +1202,9 @@ uint256 GetSequenceHash(const CTransaction& txTo) {
return ss.GetHash();
}
-uint256 GetOutputsHash(const CTransaction& txTo) {
+template <class T>
+uint256 GetOutputsHash(const T& txTo)
+{
CHashWriter ss(SER_GETHASH, 0);
for (const auto& txout : txTo.vout) {
ss << txout;
@@ -1195,7 +1214,8 @@ uint256 GetOutputsHash(const CTransaction& txTo) {
} // namespace
-PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
+template <class T>
+PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
// Cache is calculated only for transactions with witness
if (txTo.HasWitness()) {
@@ -1206,7 +1226,12 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
}
}
-uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
+// explicit instantiation
+template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
+template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
+
+template <class T>
+uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
{
assert(nIn < txTo.vin.size());
@@ -1267,7 +1292,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
}
// Wrapper to serialize only the necessary parts of the transaction being signed
- CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType);
+ CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType);
// Serialize and hash
CHashWriter ss(SER_GETHASH, 0);
@@ -1275,12 +1300,14 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig
return ss.GetHash();
}
-bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+template <class T>
+bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}
-bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
+template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
CPubKey pubkey(vchPubKey);
if (!pubkey.IsValid())
@@ -1301,7 +1328,8 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch
return true;
}
-bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const
+template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
{
// There are two kinds of nLockTime: lock-by-blockheight
// and lock-by-blocktime, distinguished by whether
@@ -1337,7 +1365,8 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
return true;
}
-bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const
+template <class T>
+bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const
{
// Relative lock times are supported by comparing the passed
// in operand to the sequence number of the input.
@@ -1383,6 +1412,10 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con
return true;
}
+// explicit instantiation
+template class GenericTransactionSignatureChecker<CTransaction>;
+template class GenericTransactionSignatureChecker<CMutableTransaction>;
+
static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
{
std::vector<std::vector<unsigned char> > stack;
diff --git a/src/script/interpreter.h b/src/script/interpreter.h
index 50c747900a..2d21aa81df 100644
--- a/src/script/interpreter.h
+++ b/src/script/interpreter.h
@@ -111,6 +111,10 @@ enum
// Public keys in segregated witness scripts must be compressed
//
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
+
+ // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
+ //
+ SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16),
};
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
@@ -120,7 +124,8 @@ struct PrecomputedTransactionData
uint256 hashPrevouts, hashSequence, hashOutputs;
bool ready = false;
- explicit PrecomputedTransactionData(const CTransaction& tx);
+ template <class T>
+ explicit PrecomputedTransactionData(const T& tx);
};
enum class SigVersion
@@ -133,7 +138,8 @@ enum class SigVersion
static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32;
static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20;
-uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
+template <class T>
+uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
class BaseSignatureChecker
{
@@ -156,10 +162,11 @@ public:
virtual ~BaseSignatureChecker() {}
};
-class TransactionSignatureChecker : public BaseSignatureChecker
+template <class T>
+class GenericTransactionSignatureChecker : public BaseSignatureChecker
{
private:
- const CTransaction* txTo;
+ const T* txTo;
unsigned int nIn;
const CAmount amount;
const PrecomputedTransactionData* txdata;
@@ -168,21 +175,15 @@ protected:
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
public:
- TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
- TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
+ GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
+ GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
};
-class MutableTransactionSignatureChecker : public TransactionSignatureChecker
-{
-private:
- const CTransaction txTo;
-
-public:
- MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {}
-};
+using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>;
+using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>;
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp
index fefa02fdef..8c26866483 100644
--- a/src/script/ismine.cpp
+++ b/src/script/ismine.cpp
@@ -28,6 +28,19 @@ enum class IsMineSigVersion
WITNESS_V0 = 2 //! P2WSH witness script execution
};
+/**
+ * This is an internal representation of isminetype + invalidity.
+ * Its order is significant, as we return the max of all explored
+ * possibilities.
+ */
+enum class IsMineResult
+{
+ NO = 0, //! Not ours
+ WATCH_ONLY = 1, //! Included in watch-only balance
+ SPENDABLE = 2, //! Included in all balances
+ INVALID = 3, //! Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
+};
+
bool PermitsUncompressed(IsMineSigVersion sigversion)
{
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
@@ -42,17 +55,13 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
return true;
}
-isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion)
+IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
{
- isInvalid = false;
+ IsMineResult ret = IsMineResult::NO;
std::vector<valtype> vSolutions;
txnouttype whichType;
- if (!Solver(scriptPubKey, whichType, vSolutions)) {
- if (keystore.HaveWatchOnly(scriptPubKey))
- return ISMINE_WATCH_UNSOLVABLE;
- return ISMINE_NO;
- }
+ Solver(scriptPubKey, whichType, vSolutions);
CKeyID keyID;
switch (whichType)
@@ -64,23 +73,25 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
- isInvalid = true;
- return ISMINE_NO;
+ return IsMineResult::INVALID;
+ }
+ if (keystore.HaveKey(keyID)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
}
- if (keystore.HaveKey(keyID))
- return ISMINE_SPENDABLE;
break;
case TX_WITNESS_V0_KEYHASH:
{
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
+ // P2WPKH inside P2WSH is invalid.
+ return IsMineResult::INVALID;
+ }
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
// We do not support bare witness outputs unless the P2SH version of it would be
// acceptable as well. This protects against matching before segwit activates.
// This also applies to the P2WSH case.
break;
}
- isminetype ret = IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, IsMineSigVersion::WITNESS_V0);
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
- return ret;
+ ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
break;
}
case TX_PUBKEYHASH:
@@ -88,26 +99,32 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
if (!PermitsUncompressed(sigversion)) {
CPubKey pubkey;
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
- isInvalid = true;
- return ISMINE_NO;
+ return IsMineResult::INVALID;
}
}
- if (keystore.HaveKey(keyID))
- return ISMINE_SPENDABLE;
+ if (keystore.HaveKey(keyID)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
+ }
break;
case TX_SCRIPTHASH:
{
+ if (sigversion != IsMineSigVersion::TOP) {
+ // P2SH inside P2WSH or P2SH is invalid.
+ return IsMineResult::INVALID;
+ }
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH);
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
- return ret;
+ ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
}
break;
}
case TX_WITNESS_V0_SCRIPTHASH:
{
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
+ // P2WSH inside P2WSH is invalid.
+ return IsMineResult::INVALID;
+ }
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
break;
}
@@ -116,9 +133,7 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0);
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
- return ret;
+ ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
}
break;
}
@@ -126,7 +141,9 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
case TX_MULTISIG:
{
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
- if (sigversion == IsMineSigVersion::TOP) break;
+ if (sigversion == IsMineSigVersion::TOP) {
+ break;
+ }
// Only consider transactions "mine" if we own ALL the
// keys involved. Multi-signature transactions that are
@@ -137,36 +154,37 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
if (!PermitsUncompressed(sigversion)) {
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i].size() != 33) {
- isInvalid = true;
- return ISMINE_NO;
+ return IsMineResult::INVALID;
}
}
}
- if (HaveKeys(keys, keystore))
- return ISMINE_SPENDABLE;
+ if (HaveKeys(keys, keystore)) {
+ ret = std::max(ret, IsMineResult::SPENDABLE);
+ }
break;
}
}
- if (keystore.HaveWatchOnly(scriptPubKey)) {
- // TODO: This could be optimized some by doing some work after the above solver
- SignatureData sigs;
- return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE;
+ if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
+ ret = std::max(ret, IsMineResult::WATCH_ONLY);
}
- return ISMINE_NO;
+ return ret;
}
} // namespace
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid)
-{
- return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP);
-}
-
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey)
{
- bool isInvalid = false;
- return IsMine(keystore, scriptPubKey, isInvalid);
+ switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
+ case IsMineResult::INVALID:
+ case IsMineResult::NO:
+ return ISMINE_NO;
+ case IsMineResult::WATCH_ONLY:
+ return ISMINE_WATCH_ONLY;
+ case IsMineResult::SPENDABLE:
+ return ISMINE_SPENDABLE;
+ }
+ assert(false);
}
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest)
diff --git a/src/script/ismine.h b/src/script/ismine.h
index 8573bdfbd2..4246da49fe 100644
--- a/src/script/ismine.h
+++ b/src/script/ismine.h
@@ -17,23 +17,13 @@ class CScript;
enum isminetype
{
ISMINE_NO = 0,
- //! Indicates that we don't know how to create a scriptSig that would solve this if we were given the appropriate private keys
- ISMINE_WATCH_UNSOLVABLE = 1,
- //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys
- ISMINE_WATCH_SOLVABLE = 2,
- ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE,
- ISMINE_SPENDABLE = 4,
+ ISMINE_WATCH_ONLY = 1,
+ ISMINE_SPENDABLE = 2,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
};
/** used for bitflags of isminetype */
typedef uint8_t isminefilter;
-/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
- * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
- * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
- * keys in SigVersion::WITNESS_V0 script, but could also be used in similar cases in the future
- */
-isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid);
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
diff --git a/src/script/script.cpp b/src/script/script.cpp
index 7f25d915a8..c84c7b8ec1 100644
--- a/src/script/script.cpp
+++ b/src/script/script.cpp
@@ -141,11 +141,6 @@ const char* GetOpName(opcodetype opcode)
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
- // Note:
- // The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum
- // as kind of implementation hack, they are *NOT* real opcodes. If found in real
- // Script, just let the default: case deal with them.
-
default:
return "OP_UNKNOWN";
}
diff --git a/src/script/script.h b/src/script/script.h
index d8b7c06013..a4f377dd94 100644
--- a/src/script/script.h
+++ b/src/script/script.h
@@ -181,13 +181,6 @@ enum opcodetype
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
-
- // template matching params
- OP_SMALLINTEGER = 0xfa,
- OP_PUBKEYS = 0xfb,
- OP_PUBKEYHASH = 0xfd,
- OP_PUBKEY = 0xfe,
-
OP_INVALIDOPCODE = 0xff,
};
diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp
index dbceb1f740..ceda740580 100644
--- a/src/script/script_error.cpp
+++ b/src/script/script_error.cpp
@@ -89,6 +89,10 @@ const char* ScriptErrorString(const ScriptError serror)
return "Witness provided for non-witness script";
case SCRIPT_ERR_WITNESS_PUBKEYTYPE:
return "Using non-compressed keys in segwit";
+ case SCRIPT_ERR_OP_CODESEPARATOR:
+ return "Using OP_CODESEPARATOR in non-witness script";
+ case SCRIPT_ERR_SIG_FINDANDDELETE:
+ return "Signature is found in scriptCode";
case SCRIPT_ERR_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT:
default: break;
diff --git a/src/script/script_error.h b/src/script/script_error.h
index 3200e94707..6982a087f4 100644
--- a/src/script/script_error.h
+++ b/src/script/script_error.h
@@ -64,6 +64,10 @@ typedef enum ScriptError_t
SCRIPT_ERR_WITNESS_UNEXPECTED,
SCRIPT_ERR_WITNESS_PUBKEYTYPE,
+ /* Constant scriptCode */
+ SCRIPT_ERR_OP_CODESEPARATOR,
+ SCRIPT_ERR_SIG_FINDANDDELETE,
+
SCRIPT_ERR_ERROR_COUNT
} ScriptError;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index ac35f17f3e..60a8a2655d 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -14,9 +14,9 @@
typedef std::vector<unsigned char> valtype;
-TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
+MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {}
-bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
+bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const
{
CKey key;
if (!provider.GetKey(address, key))
@@ -33,27 +33,51 @@ bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std
return true;
}
-static bool Sign1(const SigningProvider& provider, const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
{
- std::vector<unsigned char> vchSig;
- if (!creator.CreateSig(provider, vchSig, address, scriptCode, sigversion))
- return false;
- ret.push_back(vchSig);
- return true;
+ if (provider.GetCScript(scriptid, script)) {
+ return true;
+ }
+ // Look for scripts in SignatureData
+ if (CScriptID(sigdata.redeem_script) == scriptid) {
+ script = sigdata.redeem_script;
+ return true;
+ } else if (CScriptID(sigdata.witness_script) == scriptid) {
+ script = sigdata.witness_script;
+ return true;
+ }
+ return false;
}
-static bool SignN(const SigningProvider& provider, const std::vector<valtype>& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector<valtype>& ret, SigVersion sigversion)
+static bool GetPubKey(const SigningProvider& provider, const SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey)
{
- int nSigned = 0;
- int nRequired = multisigdata.front()[0];
- for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++)
- {
- const valtype& pubkey = multisigdata[i];
- CKeyID keyID = CPubKey(pubkey).GetID();
- if (Sign1(provider, keyID, creator, scriptCode, ret, sigversion))
- ++nSigned;
+ if (provider.GetPubKey(address, pubkey)) {
+ return true;
+ }
+ // Look for pubkey in all partial sigs
+ const auto it = sigdata.signatures.find(address);
+ if (it != sigdata.signatures.end()) {
+ pubkey = it->second.first;
+ return true;
}
- return nSigned==nRequired;
+ return false;
+}
+
+static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const CKeyID& keyid, const CScript& scriptcode, SigVersion sigversion)
+{
+ const auto it = sigdata.signatures.find(keyid);
+ if (it != sigdata.signatures.end()) {
+ sig_out = it->second.second;
+ return true;
+ }
+ if (creator.CreateSig(provider, sig_out, keyid, scriptcode, sigversion)) {
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyid, pubkey);
+ auto i = sigdata.signatures.emplace(keyid, SigPair(pubkey, sig_out));
+ assert(i.second);
+ return true;
+ }
+ return false;
}
/**
@@ -63,17 +87,17 @@ static bool SignN(const SigningProvider& provider, const std::vector<valtype>& m
* Returns false if scriptPubKey could not be completely satisfied.
*/
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
- std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion)
+ std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
ret.clear();
+ std::vector<unsigned char> sig;
std::vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
return false;
- CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
@@ -81,37 +105,47 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
case TX_WITNESS_UNKNOWN:
return false;
case TX_PUBKEY:
- keyID = CPubKey(vSolutions[0]).GetID();
- return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion);
- case TX_PUBKEYHASH:
- keyID = CKeyID(uint160(vSolutions[0]));
- if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion))
- return false;
- else
- {
- CPubKey vch;
- provider.GetPubKey(keyID, vch);
- ret.push_back(ToByteVector(vch));
- }
+ if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]).GetID(), scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
+ return true;
+ case TX_PUBKEYHASH: {
+ CKeyID keyID = CKeyID(uint160(vSolutions[0]));
+ if (!CreateSig(creator, sigdata, provider, sig, keyID, scriptPubKey, sigversion)) return false;
+ ret.push_back(std::move(sig));
+ CPubKey pubkey;
+ GetPubKey(provider, sigdata, keyID, pubkey);
+ ret.push_back(ToByteVector(pubkey));
return true;
+ }
case TX_SCRIPTHASH:
- if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) {
+ if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
return false;
- case TX_MULTISIG:
+ case TX_MULTISIG: {
+ size_t required = vSolutions.front()[0];
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
- return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion));
-
+ for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
+ CPubKey pubkey = CPubKey(vSolutions[i]);
+ if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey.GetID(), scriptPubKey, sigversion)) {
+ ret.push_back(std::move(sig));
+ }
+ }
+ bool ok = ret.size() == required + 1;
+ for (size_t i = 0; i + ret.size() < required + 1; ++i) {
+ ret.push_back(valtype());
+ }
+ return ok;
+ }
case TX_WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
case TX_WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (provider.GetCScript(h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, h160, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -139,9 +173,11 @@ static CScript PushAll(const std::vector<valtype>& values)
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata)
{
+ if (sigdata.complete) return true;
+
std::vector<valtype> result;
txnouttype whichType;
- bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE);
+ bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
@@ -152,7 +188,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
- solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH;
+ sigdata.redeem_script = subscript;
+ solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
P2SH = true;
}
@@ -161,15 +198,16 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0);
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
result.clear();
}
else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
+ sigdata.witness_script = witnessscript;
txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
result.clear();
@@ -181,106 +219,29 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
sigdata.scriptSig = PushAll(result);
// Test solution
- return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
-}
-
-SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn)
-{
- SignatureData data;
- assert(tx.vin.size() > nIn);
- data.scriptSig = tx.vin[nIn].scriptSig;
- data.scriptWitness = tx.vin[nIn].scriptWitness;
- return data;
+ sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker());
+ return sigdata.complete;
}
-void UpdateInput(CTxIn& input, const SignatureData& data)
+class SignatureExtractorChecker final : public BaseSignatureChecker
{
- input.scriptSig = data.scriptSig;
- input.scriptWitness = data.scriptWitness;
-}
-
-void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data)
-{
- assert(tx.vin.size() > nIn);
- UpdateInput(tx.vin[nIn], data);
-}
-
-bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
-{
- assert(nIn < txTo.vin.size());
+private:
+ SignatureData& sigdata;
+ BaseSignatureChecker& checker;
- CTransaction txToConst(txTo);
- TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType);
-
- SignatureData sigdata;
- bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
- UpdateTransaction(txTo, nIn, sigdata);
- return ret;
-}
-
-bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
-{
- assert(nIn < txTo.vin.size());
- CTxIn& txin = txTo.vin[nIn];
- assert(txin.prevout.n < txFrom.vout.size());
- const CTxOut& txout = txFrom.vout[txin.prevout.n];
-
- return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
-}
+public:
+ SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {}
+ bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
+};
-static std::vector<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const std::vector<valtype>& vSolutions,
- const std::vector<valtype>& sigs1, const std::vector<valtype>& sigs2, SigVersion sigversion)
+bool SignatureExtractorChecker::CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
- // Combine all the signatures we've got:
- std::set<valtype> allsigs;
- for (const valtype& v : sigs1)
- {
- if (!v.empty())
- allsigs.insert(v);
- }
- for (const valtype& v : sigs2)
- {
- if (!v.empty())
- allsigs.insert(v);
- }
-
- // Build a map of pubkey -> signature by matching sigs to pubkeys:
- assert(vSolutions.size() > 1);
- unsigned int nSigsRequired = vSolutions.front()[0];
- unsigned int nPubKeys = vSolutions.size()-2;
- std::map<valtype, valtype> sigs;
- for (const valtype& sig : allsigs)
- {
- for (unsigned int i = 0; i < nPubKeys; i++)
- {
- const valtype& pubkey = vSolutions[i+1];
- if (sigs.count(pubkey))
- continue; // Already got a sig for this pubkey
-
- if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion))
- {
- sigs[pubkey] = sig;
- break;
- }
- }
- }
- // Now build a merged CScript:
- unsigned int nSigsHave = 0;
- std::vector<valtype> result; result.push_back(valtype()); // pop-one-too-many workaround
- for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++)
- {
- if (sigs.count(vSolutions[i+1]))
- {
- result.push_back(sigs[vSolutions[i+1]]);
- ++nSigsHave;
- }
+ if (checker.CheckSig(scriptSig, vchPubKey, scriptCode, sigversion)) {
+ CPubKey pubkey(vchPubKey);
+ sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig));
+ return true;
}
- // Fill any missing with OP_0:
- for (unsigned int i = nSigsHave; i < nSigsRequired; i++)
- result.push_back(valtype());
-
- return result;
+ return false;
}
namespace
@@ -305,89 +266,115 @@ struct Stacks
};
}
-static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const txnouttype txType, const std::vector<valtype>& vSolutions,
- Stacks sigs1, Stacks sigs2, SigVersion sigversion)
+// Extracts signatures and scripts from incomplete scriptSigs. Please do not extend this, use PSBT instead
+SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout)
{
- switch (txType)
- {
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
- // Don't know anything about this, assume bigger one is correct:
- if (sigs1.script.size() >= sigs2.script.size())
- return sigs1;
- return sigs2;
- case TX_PUBKEY:
- case TX_PUBKEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.script.empty() || sigs1.script[0].empty())
- return sigs2;
- return sigs1;
- case TX_WITNESS_V0_KEYHASH:
- // Signatures are bigger than placeholders or empty scripts:
- if (sigs1.witness.empty() || sigs1.witness[0].empty())
- return sigs2;
- return sigs1;
- case TX_SCRIPTHASH:
- if (sigs1.script.empty() || sigs1.script.back().empty())
- return sigs2;
- else if (sigs2.script.empty() || sigs2.script.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- valtype spk = sigs1.script.back();
- CScript pubKey2(spk.begin(), spk.end());
-
- txnouttype txType2;
- std::vector<std::vector<unsigned char> > vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.script.pop_back();
- sigs2.script.pop_back();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion);
- result.script.push_back(spk);
- return result;
- }
- case TX_MULTISIG:
- return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion));
- case TX_WITNESS_V0_SCRIPTHASH:
- if (sigs1.witness.empty() || sigs1.witness.back().empty())
- return sigs2;
- else if (sigs2.witness.empty() || sigs2.witness.back().empty())
- return sigs1;
- else
- {
- // Recur to combine:
- CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end());
- txnouttype txType2;
- std::vector<valtype> vSolutions2;
- Solver(pubKey2, txType2, vSolutions2);
- sigs1.witness.pop_back();
- sigs1.script = sigs1.witness;
- sigs1.witness.clear();
- sigs2.witness.pop_back();
- sigs2.script = sigs2.witness;
- sigs2.witness.clear();
- Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, SigVersion::WITNESS_V0);
- result.witness = result.script;
- result.script.clear();
- result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end()));
- return result;
+ SignatureData data;
+ assert(tx.vin.size() > nIn);
+ data.scriptSig = tx.vin[nIn].scriptSig;
+ data.scriptWitness = tx.vin[nIn].scriptWitness;
+ Stacks stack(data);
+
+ // Get signatures
+ MutableTransactionSignatureChecker tx_checker(&tx, nIn, txout.nValue);
+ SignatureExtractorChecker extractor_checker(data, tx_checker);
+ if (VerifyScript(data.scriptSig, txout.scriptPubKey, &data.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, extractor_checker)) {
+ data.complete = true;
+ return data;
+ }
+
+ // Get scripts
+ txnouttype script_type;
+ std::vector<std::vector<unsigned char>> solutions;
+ Solver(txout.scriptPubKey, script_type, solutions);
+ SigVersion sigversion = SigVersion::BASE;
+ CScript next_script = txout.scriptPubKey;
+
+ if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
+ // Get the redeemScript
+ CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
+ data.redeem_script = redeem_script;
+ next_script = std::move(redeem_script);
+
+ // Get redeemScript type
+ Solver(next_script, script_type, solutions);
+ stack.script.pop_back();
+ }
+ if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
+ // Get the witnessScript
+ CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
+ data.witness_script = witness_script;
+ next_script = std::move(witness_script);
+
+ // Get witnessScript type
+ Solver(next_script, script_type, solutions);
+ stack.witness.pop_back();
+ stack.script = std::move(stack.witness);
+ stack.witness.clear();
+ sigversion = SigVersion::WITNESS_V0;
+ }
+ if (script_type == TX_MULTISIG && !stack.script.empty()) {
+ // Build a map of pubkey -> signature by matching sigs to pubkeys:
+ assert(solutions.size() > 1);
+ unsigned int num_pubkeys = solutions.size()-2;
+ unsigned int last_success_key = 0;
+ for (const valtype& sig : stack.script) {
+ for (unsigned int i = last_success_key; i < num_pubkeys; ++i) {
+ const valtype& pubkey = solutions[i+1];
+ // We either have a signature for this pubkey, or we have found a signature and it is valid
+ if (data.signatures.count(CPubKey(pubkey).GetID()) || extractor_checker.CheckSig(sig, pubkey, next_script, sigversion)) {
+ last_success_key = i + 1;
+ break;
+ }
+ }
}
- default:
- return Stacks();
}
+
+ return data;
+}
+
+void UpdateInput(CTxIn& input, const SignatureData& data)
+{
+ input.scriptSig = data.scriptSig;
+ input.scriptWitness = data.scriptWitness;
+}
+
+void SignatureData::MergeSignatureData(SignatureData sigdata)
+{
+ if (complete) return;
+ if (sigdata.complete) {
+ *this = std::move(sigdata);
+ return;
+ }
+ if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
+ redeem_script = sigdata.redeem_script;
+ }
+ if (witness_script.empty() && !sigdata.witness_script.empty()) {
+ witness_script = sigdata.witness_script;
+ }
+ signatures.insert(std::make_move_iterator(sigdata.signatures.begin()), std::make_move_iterator(sigdata.signatures.end()));
}
-SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
- const SignatureData& scriptSig1, const SignatureData& scriptSig2)
+bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType)
+{
+ assert(nIn < txTo.vin.size());
+
+ MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType);
+
+ SignatureData sigdata;
+ bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata);
+ UpdateInput(txTo.vin.at(nIn), sigdata);
+ return ret;
+}
+
+bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType)
{
- txnouttype txType;
- std::vector<std::vector<unsigned char> > vSolutions;
- Solver(scriptPubKey, txType, vSolutions);
+ assert(nIn < txTo.vin.size());
+ CTxIn& txin = txTo.vin[nIn];
+ assert(txin.prevout.n < txFrom.vout.size());
+ const CTxOut& txout = txFrom.vout[txin.prevout.n];
- return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), SigVersion::BASE).Output();
+ return SignSignature(provider, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType);
}
namespace {
@@ -423,6 +410,7 @@ public:
}
const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR = DummySignatureCreator();
+const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
bool IsSolvable(const SigningProvider& provider, const CScript& script)
{
diff --git a/src/script/sign.h b/src/script/sign.h
index cf3651c1de..3666859641 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -21,11 +21,13 @@ class SigningProvider
{
public:
virtual ~SigningProvider() {}
- virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const =0;
- virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const =0;
- virtual bool GetKey(const CKeyID &address, CKey& key) const =0;
+ virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; }
+ virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; }
+ virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; }
};
+extern const SigningProvider& DUMMY_SIGNING_PROVIDER;
+
/** Interface for signature creators. */
class BaseSignatureCreator {
public:
@@ -37,35 +39,38 @@ public:
};
/** A signature creator for transactions. */
-class TransactionSignatureCreator : public BaseSignatureCreator {
- const CTransaction* txTo;
+class MutableTransactionSignatureCreator : public BaseSignatureCreator {
+ const CMutableTransaction* txTo;
unsigned int nIn;
int nHashType;
CAmount amount;
- const TransactionSignatureChecker checker;
+ const MutableTransactionSignatureChecker checker;
public:
- TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL);
+ MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL);
const BaseSignatureChecker& Checker() const override { return checker; }
bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
};
-class MutableTransactionSignatureCreator : public TransactionSignatureCreator {
- CTransaction tx;
-
-public:
- MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {}
-};
-
/** A signature creator that just produces 72-byte empty signatures. */
extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR;
+typedef std::pair<CPubKey, std::vector<unsigned char>> SigPair;
+
+// This struct contains information from a transaction input and also contains signatures for that input.
+// The information contained here can be used to create a signature and is also filled by ProduceSignature
+// in order to construct final scriptSigs and scriptWitnesses.
struct SignatureData {
- CScript scriptSig;
- CScriptWitness scriptWitness;
+ bool complete = false; ///< Stores whether the scriptSig and scriptWitness are complete
+ CScript scriptSig; ///< The scriptSig of an input. Contains complete signatures or the traditional partial signatures format
+ CScript redeem_script; ///< The redeemScript (if any) for the input
+ CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
+ CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
+ std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
SignatureData() {}
explicit SignatureData(const CScript& script) : scriptSig(script) {}
+ void MergeSignatureData(SignatureData sigdata);
};
/** Produce a script signature using a generic signature creator. */
@@ -75,12 +80,8 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType);
bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType);
-/** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */
-SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2);
-
-/** Extract signature data from a transaction, and insert it. */
-SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
-void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data);
+/** Extract signature data from a transaction input, and insert it. */
+SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout);
void UpdateInput(CTxIn& input, const SignatureData& data);
/* Check whether we know how to sign for an output like this, assuming we
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 76778112aa..f0b2c62a91 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -5,6 +5,7 @@
#include <script/standard.h>
+#include <crypto/sha256.h>
#include <pubkey.h>
#include <script/script.h>
#include <util.h>
@@ -18,6 +19,11 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
+{
+ CSHA256().Write(in.data(), in.size()).Finalize(begin());
+}
+
const char* GetTxnOutputType(txnouttype t)
{
switch (t)
@@ -35,22 +41,54 @@ const char* GetTxnOutputType(txnouttype t)
return nullptr;
}
-bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
+static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
{
- // Templates
- static std::multimap<txnouttype, CScript> mTemplates;
- if (mTemplates.empty())
- {
- // Standard tx, sender provides pubkey, receiver adds signature
- mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
+ if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1);
+ return CPubKey::ValidSize(pubkey);
+ }
+ if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1);
+ return CPubKey::ValidSize(pubkey);
+ }
+ return false;
+}
- // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
- mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
+static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
+{
+ if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
+ pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
+ return true;
+ }
+ return false;
+}
- // Sender provides N pubkeys, receivers provides M signatures
- mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
+/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
+static constexpr bool IsSmallInteger(opcodetype opcode)
+{
+ return opcode >= OP_1 && opcode <= OP_16;
+}
+
+static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
+{
+ opcodetype opcode;
+ valtype data;
+ CScript::const_iterator it = script.begin();
+ if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
+
+ if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false;
+ required = CScript::DecodeOP_N(opcode);
+ while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
+ pubkeys.emplace_back(std::move(data));
}
+ if (!IsSmallInteger(opcode)) return false;
+ unsigned int keys = CScript::DecodeOP_N(opcode);
+ if (pubkeys.size() != keys || keys < required) return false;
+ return (it + 1 == script.end());
+}
+bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
+{
vSolutionsRet.clear();
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
@@ -82,6 +120,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
vSolutionsRet.push_back(std::move(witnessprogram));
return true;
}
+ typeRet = TX_NONSTANDARD;
return false;
}
@@ -95,84 +134,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
return true;
}
- // Scan templates
- const CScript& script1 = scriptPubKey;
- for (const std::pair<txnouttype, CScript>& tplate : mTemplates)
- {
- const CScript& script2 = tplate.second;
- vSolutionsRet.clear();
+ std::vector<unsigned char> data;
+ if (MatchPayToPubkey(scriptPubKey, data)) {
+ typeRet = TX_PUBKEY;
+ vSolutionsRet.push_back(std::move(data));
+ return true;
+ }
- opcodetype opcode1, opcode2;
- std::vector<unsigned char> vch1, vch2;
+ if (MatchPayToPubkeyHash(scriptPubKey, data)) {
+ typeRet = TX_PUBKEYHASH;
+ vSolutionsRet.push_back(std::move(data));
+ return true;
+ }
- // Compare
- CScript::const_iterator pc1 = script1.begin();
- CScript::const_iterator pc2 = script2.begin();
- while (true)
- {
- if (pc1 == script1.end() && pc2 == script2.end())
- {
- // Found a match
- typeRet = tplate.first;
- if (typeRet == TX_MULTISIG)
- {
- // Additional checks for TX_MULTISIG:
- unsigned char m = vSolutionsRet.front()[0];
- unsigned char n = vSolutionsRet.back()[0];
- if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
- return false;
- }
- return true;
- }
- if (!script1.GetOp(pc1, opcode1, vch1))
- break;
- if (!script2.GetOp(pc2, opcode2, vch2))
- break;
-
- // Template matching opcodes:
- if (opcode2 == OP_PUBKEYS)
- {
- while (CPubKey::ValidSize(vch1))
- {
- vSolutionsRet.push_back(vch1);
- if (!script1.GetOp(pc1, opcode1, vch1))
- break;
- }
- if (!script2.GetOp(pc2, opcode2, vch2))
- break;
- // Normal situation is to fall through
- // to other if/else statements
- }
-
- if (opcode2 == OP_PUBKEY)
- {
- if (!CPubKey::ValidSize(vch1))
- break;
- vSolutionsRet.push_back(vch1);
- }
- else if (opcode2 == OP_PUBKEYHASH)
- {
- if (vch1.size() != sizeof(uint160))
- break;
- vSolutionsRet.push_back(vch1);
- }
- else if (opcode2 == OP_SMALLINTEGER)
- { // Single-byte small integer pushed onto vSolutions
- if (opcode1 == OP_0 ||
- (opcode1 >= OP_1 && opcode1 <= OP_16))
- {
- char n = (char)CScript::DecodeOP_N(opcode1);
- vSolutionsRet.push_back(valtype(1, n));
- }
- else
- break;
- }
- else if (opcode1 != opcode2 || vch1 != vch2)
- {
- // Others must match exactly
- break;
- }
- }
+ unsigned int required;
+ std::vector<std::vector<unsigned char>> keys;
+ if (MatchMultisig(scriptPubKey, required, keys)) {
+ typeRet = TX_MULTISIG;
+ vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
+ vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
+ vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
+ return true;
}
vSolutionsRet.clear();
@@ -353,9 +335,7 @@ CScript GetScriptForWitness(const CScript& redeemscript)
return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
}
}
- uint256 hash;
- CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin());
- return GetScriptForDestination(WitnessV0ScriptHash(hash));
+ return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
}
bool IsValidDestination(const CTxDestination& dest) {
diff --git a/src/script/standard.h b/src/script/standard.h
index 4922b7236b..1380030871 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -77,6 +77,7 @@ struct WitnessV0ScriptHash : public uint256
{
WitnessV0ScriptHash() : uint256() {}
explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
+ explicit WitnessV0ScriptHash(const CScript& script);
using uint256::uint256;
};