Browse Source
0.10e088d65
Separate script/sign (jtimon)9294a4b
Separate CScriptCompressor (jtimon)c4408a6
Separate script/standard (jtimon)da03e6e
Separate script/interpreter (jtimon)cbd22a5
Move CScript class and dependencies to script/script (jtimon)86dbeea
Rename script.h/.cpp to scriptutils.h/.cpp (plus remove duplicated includes) (jtimon) Rebased-by: Pieter Wuille
Pieter Wuille
10 years ago
37 changed files with 1513 additions and 1387 deletions
@ -0,0 +1,127 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "compressor.h" |
||||||
|
|
||||||
|
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(CPubKey &pubkey) const |
||||||
|
{ |
||||||
|
if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG |
||||||
|
&& (script[1] == 0x02 || script[1] == 0x03)) { |
||||||
|
pubkey.Set(&script[1], &script[34]); |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG |
||||||
|
&& script[1] == 0x04) { |
||||||
|
pubkey.Set(&script[1], &script[66]); |
||||||
|
return pubkey.IsFullyValid(); // if not fully valid, 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; |
||||||
|
} |
||||||
|
CPubKey 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: |
||||||
|
unsigned char vch[33] = {}; |
||||||
|
vch[0] = nSize - 2; |
||||||
|
memcpy(&vch[1], &in[0], 32); |
||||||
|
CPubKey pubkey(&vch[0], &vch[33]); |
||||||
|
if (!pubkey.Decompress()) |
||||||
|
return false; |
||||||
|
assert(pubkey.size() == 65); |
||||||
|
script.resize(67); |
||||||
|
script[0] = 65; |
||||||
|
memcpy(&script[1], pubkey.begin(), 65); |
||||||
|
script[66] = OP_CHECKSIG; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_SCRIPT_COMPRESSOR |
||||||
|
#define H_BITCOIN_SCRIPT_COMPRESSOR |
||||||
|
|
||||||
|
#include "script/script.h" |
||||||
|
|
||||||
|
/** Compact serializer for scripts.
|
||||||
|
* |
||||||
|
* It detects common cases and encodes them much more efficiently. |
||||||
|
* 3 special cases are defined: |
||||||
|
* * Pay to pubkey hash (encoded as 21 bytes) |
||||||
|
* * Pay to script hash (encoded as 21 bytes) |
||||||
|
* * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) |
||||||
|
* |
||||||
|
* Other scripts up to 121 bytes require 1 byte + script length. Above |
||||||
|
* that, scripts up to 16505 bytes require 2 bytes + script length. |
||||||
|
*/ |
||||||
|
class CScriptCompressor |
||||||
|
{ |
||||||
|
private: |
||||||
|
// make this static for now (there are only 6 special scripts defined)
|
||||||
|
// this can potentially be extended together with a new nVersion for
|
||||||
|
// transactions, in which case this value becomes dependent on nVersion
|
||||||
|
// and nHeight of the enclosing transaction.
|
||||||
|
static const unsigned int nSpecialScripts = 6; |
||||||
|
|
||||||
|
CScript &script; |
||||||
|
protected: |
||||||
|
// These check for scripts for which a special case with a shorter encoding is defined.
|
||||||
|
// They are implemented separately from the CScript test, as these test for exact byte
|
||||||
|
// sequence correspondences, and are more strict. For example, IsToPubKey also verifies
|
||||||
|
// whether the public key is valid (as invalid ones cannot be represented in compressed
|
||||||
|
// form).
|
||||||
|
bool IsToKeyID(CKeyID &hash) const; |
||||||
|
bool IsToScriptID(CScriptID &hash) const; |
||||||
|
bool IsToPubKey(CPubKey &pubkey) const; |
||||||
|
|
||||||
|
bool Compress(std::vector<unsigned char> &out) const; |
||||||
|
unsigned int GetSpecialSize(unsigned int nSize) const; |
||||||
|
bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out); |
||||||
|
public: |
||||||
|
CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } |
||||||
|
|
||||||
|
unsigned int GetSerializeSize(int nType, int nVersion) const { |
||||||
|
std::vector<unsigned char> compr; |
||||||
|
if (Compress(compr)) |
||||||
|
return compr.size(); |
||||||
|
unsigned int nSize = script.size() + nSpecialScripts; |
||||||
|
return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename Stream> |
||||||
|
void Serialize(Stream &s, int nType, int nVersion) const { |
||||||
|
std::vector<unsigned char> compr; |
||||||
|
if (Compress(compr)) { |
||||||
|
s << CFlatData(compr); |
||||||
|
return; |
||||||
|
} |
||||||
|
unsigned int nSize = script.size() + nSpecialScripts; |
||||||
|
s << VARINT(nSize); |
||||||
|
s << CFlatData(script); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename Stream> |
||||||
|
void Unserialize(Stream &s, int nType, int nVersion) { |
||||||
|
unsigned int nSize = 0; |
||||||
|
s >> VARINT(nSize); |
||||||
|
if (nSize < nSpecialScripts) { |
||||||
|
std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00); |
||||||
|
s >> REF(CFlatData(vch)); |
||||||
|
Decompress(nSize, vch); |
||||||
|
return; |
||||||
|
} |
||||||
|
nSize -= nSpecialScripts; |
||||||
|
script.resize(nSize); |
||||||
|
s >> REF(CFlatData(script)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,45 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_SCRIPT_INTERPRETER |
||||||
|
#define H_BITCOIN_SCRIPT_INTERPRETER |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
class uint256; |
||||||
|
class CScript; |
||||||
|
class CTransaction; |
||||||
|
|
||||||
|
/** Signature hash types/flags */ |
||||||
|
enum |
||||||
|
{ |
||||||
|
SIGHASH_ALL = 1, |
||||||
|
SIGHASH_NONE = 2, |
||||||
|
SIGHASH_SINGLE = 3, |
||||||
|
SIGHASH_ANYONECANPAY = 0x80, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Script verification flags */ |
||||||
|
enum |
||||||
|
{ |
||||||
|
SCRIPT_VERIFY_NONE = 0, |
||||||
|
SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts
|
||||||
|
SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys
|
||||||
|
SCRIPT_VERIFY_LOW_S = (1U << 2), // enforce low S values (<n/2) in signatures (depends on STRICTENC)
|
||||||
|
SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
|
||||||
|
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
|
||||||
|
}; |
||||||
|
|
||||||
|
bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags); |
||||||
|
bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags); |
||||||
|
|
||||||
|
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); |
||||||
|
bool CheckSig(std::vector<unsigned char> vchSig, const std::vector<unsigned char> &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); |
||||||
|
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); |
||||||
|
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,295 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "script.h" |
||||||
|
|
||||||
|
#include <boost/foreach.hpp> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
const char* GetOpName(opcodetype opcode) |
||||||
|
{ |
||||||
|
switch (opcode) |
||||||
|
{ |
||||||
|
// push value
|
||||||
|
case OP_0 : return "0"; |
||||||
|
case OP_PUSHDATA1 : return "OP_PUSHDATA1"; |
||||||
|
case OP_PUSHDATA2 : return "OP_PUSHDATA2"; |
||||||
|
case OP_PUSHDATA4 : return "OP_PUSHDATA4"; |
||||||
|
case OP_1NEGATE : return "-1"; |
||||||
|
case OP_RESERVED : return "OP_RESERVED"; |
||||||
|
case OP_1 : return "1"; |
||||||
|
case OP_2 : return "2"; |
||||||
|
case OP_3 : return "3"; |
||||||
|
case OP_4 : return "4"; |
||||||
|
case OP_5 : return "5"; |
||||||
|
case OP_6 : return "6"; |
||||||
|
case OP_7 : return "7"; |
||||||
|
case OP_8 : return "8"; |
||||||
|
case OP_9 : return "9"; |
||||||
|
case OP_10 : return "10"; |
||||||
|
case OP_11 : return "11"; |
||||||
|
case OP_12 : return "12"; |
||||||
|
case OP_13 : return "13"; |
||||||
|
case OP_14 : return "14"; |
||||||
|
case OP_15 : return "15"; |
||||||
|
case OP_16 : return "16"; |
||||||
|
|
||||||
|
// control
|
||||||
|
case OP_NOP : return "OP_NOP"; |
||||||
|
case OP_VER : return "OP_VER"; |
||||||
|
case OP_IF : return "OP_IF"; |
||||||
|
case OP_NOTIF : return "OP_NOTIF"; |
||||||
|
case OP_VERIF : return "OP_VERIF"; |
||||||
|
case OP_VERNOTIF : return "OP_VERNOTIF"; |
||||||
|
case OP_ELSE : return "OP_ELSE"; |
||||||
|
case OP_ENDIF : return "OP_ENDIF"; |
||||||
|
case OP_VERIFY : return "OP_VERIFY"; |
||||||
|
case OP_RETURN : return "OP_RETURN"; |
||||||
|
|
||||||
|
// stack ops
|
||||||
|
case OP_TOALTSTACK : return "OP_TOALTSTACK"; |
||||||
|
case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; |
||||||
|
case OP_2DROP : return "OP_2DROP"; |
||||||
|
case OP_2DUP : return "OP_2DUP"; |
||||||
|
case OP_3DUP : return "OP_3DUP"; |
||||||
|
case OP_2OVER : return "OP_2OVER"; |
||||||
|
case OP_2ROT : return "OP_2ROT"; |
||||||
|
case OP_2SWAP : return "OP_2SWAP"; |
||||||
|
case OP_IFDUP : return "OP_IFDUP"; |
||||||
|
case OP_DEPTH : return "OP_DEPTH"; |
||||||
|
case OP_DROP : return "OP_DROP"; |
||||||
|
case OP_DUP : return "OP_DUP"; |
||||||
|
case OP_NIP : return "OP_NIP"; |
||||||
|
case OP_OVER : return "OP_OVER"; |
||||||
|
case OP_PICK : return "OP_PICK"; |
||||||
|
case OP_ROLL : return "OP_ROLL"; |
||||||
|
case OP_ROT : return "OP_ROT"; |
||||||
|
case OP_SWAP : return "OP_SWAP"; |
||||||
|
case OP_TUCK : return "OP_TUCK"; |
||||||
|
|
||||||
|
// splice ops
|
||||||
|
case OP_CAT : return "OP_CAT"; |
||||||
|
case OP_SUBSTR : return "OP_SUBSTR"; |
||||||
|
case OP_LEFT : return "OP_LEFT"; |
||||||
|
case OP_RIGHT : return "OP_RIGHT"; |
||||||
|
case OP_SIZE : return "OP_SIZE"; |
||||||
|
|
||||||
|
// bit logic
|
||||||
|
case OP_INVERT : return "OP_INVERT"; |
||||||
|
case OP_AND : return "OP_AND"; |
||||||
|
case OP_OR : return "OP_OR"; |
||||||
|
case OP_XOR : return "OP_XOR"; |
||||||
|
case OP_EQUAL : return "OP_EQUAL"; |
||||||
|
case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; |
||||||
|
case OP_RESERVED1 : return "OP_RESERVED1"; |
||||||
|
case OP_RESERVED2 : return "OP_RESERVED2"; |
||||||
|
|
||||||
|
// numeric
|
||||||
|
case OP_1ADD : return "OP_1ADD"; |
||||||
|
case OP_1SUB : return "OP_1SUB"; |
||||||
|
case OP_2MUL : return "OP_2MUL"; |
||||||
|
case OP_2DIV : return "OP_2DIV"; |
||||||
|
case OP_NEGATE : return "OP_NEGATE"; |
||||||
|
case OP_ABS : return "OP_ABS"; |
||||||
|
case OP_NOT : return "OP_NOT"; |
||||||
|
case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; |
||||||
|
case OP_ADD : return "OP_ADD"; |
||||||
|
case OP_SUB : return "OP_SUB"; |
||||||
|
case OP_MUL : return "OP_MUL"; |
||||||
|
case OP_DIV : return "OP_DIV"; |
||||||
|
case OP_MOD : return "OP_MOD"; |
||||||
|
case OP_LSHIFT : return "OP_LSHIFT"; |
||||||
|
case OP_RSHIFT : return "OP_RSHIFT"; |
||||||
|
case OP_BOOLAND : return "OP_BOOLAND"; |
||||||
|
case OP_BOOLOR : return "OP_BOOLOR"; |
||||||
|
case OP_NUMEQUAL : return "OP_NUMEQUAL"; |
||||||
|
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; |
||||||
|
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; |
||||||
|
case OP_LESSTHAN : return "OP_LESSTHAN"; |
||||||
|
case OP_GREATERTHAN : return "OP_GREATERTHAN"; |
||||||
|
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; |
||||||
|
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; |
||||||
|
case OP_MIN : return "OP_MIN"; |
||||||
|
case OP_MAX : return "OP_MAX"; |
||||||
|
case OP_WITHIN : return "OP_WITHIN"; |
||||||
|
|
||||||
|
// crypto
|
||||||
|
case OP_RIPEMD160 : return "OP_RIPEMD160"; |
||||||
|
case OP_SHA1 : return "OP_SHA1"; |
||||||
|
case OP_SHA256 : return "OP_SHA256"; |
||||||
|
case OP_HASH160 : return "OP_HASH160"; |
||||||
|
case OP_HASH256 : return "OP_HASH256"; |
||||||
|
case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; |
||||||
|
case OP_CHECKSIG : return "OP_CHECKSIG"; |
||||||
|
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; |
||||||
|
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; |
||||||
|
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; |
||||||
|
|
||||||
|
// expanson
|
||||||
|
case OP_NOP1 : return "OP_NOP1"; |
||||||
|
case OP_NOP2 : return "OP_NOP2"; |
||||||
|
case OP_NOP3 : return "OP_NOP3"; |
||||||
|
case OP_NOP4 : return "OP_NOP4"; |
||||||
|
case OP_NOP5 : return "OP_NOP5"; |
||||||
|
case OP_NOP6 : return "OP_NOP6"; |
||||||
|
case OP_NOP7 : return "OP_NOP7"; |
||||||
|
case OP_NOP8 : return "OP_NOP8"; |
||||||
|
case OP_NOP9 : return "OP_NOP9"; |
||||||
|
case OP_NOP10 : return "OP_NOP10"; |
||||||
|
|
||||||
|
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; |
||||||
|
|
||||||
|
// Note:
|
||||||
|
// The template matching params OP_SMALLDATA/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"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int CScript::GetSigOpCount(bool fAccurate) const |
||||||
|
{ |
||||||
|
unsigned int n = 0; |
||||||
|
const_iterator pc = begin(); |
||||||
|
opcodetype lastOpcode = OP_INVALIDOPCODE; |
||||||
|
while (pc < end()) |
||||||
|
{ |
||||||
|
opcodetype opcode; |
||||||
|
if (!GetOp(pc, opcode)) |
||||||
|
break; |
||||||
|
if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) |
||||||
|
n++; |
||||||
|
else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) |
||||||
|
{ |
||||||
|
if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) |
||||||
|
n += DecodeOP_N(lastOpcode); |
||||||
|
else |
||||||
|
n += 20; |
||||||
|
} |
||||||
|
lastOpcode = opcode; |
||||||
|
} |
||||||
|
return n; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const |
||||||
|
{ |
||||||
|
if (!IsPayToScriptHash()) |
||||||
|
return GetSigOpCount(true); |
||||||
|
|
||||||
|
// This is a pay-to-script-hash scriptPubKey;
|
||||||
|
// get the last item that the scriptSig
|
||||||
|
// pushes onto the stack:
|
||||||
|
const_iterator pc = scriptSig.begin(); |
||||||
|
vector<unsigned char> data; |
||||||
|
while (pc < scriptSig.end()) |
||||||
|
{ |
||||||
|
opcodetype opcode; |
||||||
|
if (!scriptSig.GetOp(pc, opcode, data)) |
||||||
|
return 0; |
||||||
|
if (opcode > OP_16) |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/// ... and return its opcount:
|
||||||
|
CScript subscript(data.begin(), data.end()); |
||||||
|
return subscript.GetSigOpCount(true); |
||||||
|
} |
||||||
|
|
||||||
|
bool CScript::IsPayToScriptHash() const |
||||||
|
{ |
||||||
|
// Extra-fast test for pay-to-script-hash CScripts:
|
||||||
|
return (this->size() == 23 && |
||||||
|
this->at(0) == OP_HASH160 && |
||||||
|
this->at(1) == 0x14 && |
||||||
|
this->at(22) == OP_EQUAL); |
||||||
|
} |
||||||
|
|
||||||
|
bool CScript::IsPushOnly() const |
||||||
|
{ |
||||||
|
const_iterator pc = begin(); |
||||||
|
while (pc < end()) |
||||||
|
{ |
||||||
|
opcodetype opcode; |
||||||
|
if (!GetOp(pc, opcode)) |
||||||
|
return false; |
||||||
|
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
|
||||||
|
// push-type opcode, however execution of OP_RESERVED fails, so
|
||||||
|
// it's not relevant to P2SH as the scriptSig would fail prior to
|
||||||
|
// the P2SH special validation code being executed.
|
||||||
|
if (opcode > OP_16) |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool CScript::HasCanonicalPushes() const |
||||||
|
{ |
||||||
|
const_iterator pc = begin(); |
||||||
|
while (pc < end()) |
||||||
|
{ |
||||||
|
opcodetype opcode; |
||||||
|
std::vector<unsigned char> data; |
||||||
|
if (!GetOp(pc, opcode, data)) |
||||||
|
return false; |
||||||
|
if (opcode > OP_16) |
||||||
|
continue; |
||||||
|
if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) |
||||||
|
// Could have used an OP_n code, rather than a 1-byte push.
|
||||||
|
return false; |
||||||
|
if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) |
||||||
|
// Could have used a normal n-byte push, rather than OP_PUSHDATA1.
|
||||||
|
return false; |
||||||
|
if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) |
||||||
|
// Could have used an OP_PUSHDATA1.
|
||||||
|
return false; |
||||||
|
if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) |
||||||
|
// Could have used an OP_PUSHDATA2.
|
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
class CScriptVisitor : public boost::static_visitor<bool> |
||||||
|
{ |
||||||
|
private: |
||||||
|
CScript *script; |
||||||
|
public: |
||||||
|
CScriptVisitor(CScript *scriptin) { script = scriptin; } |
||||||
|
|
||||||
|
bool operator()(const CNoDestination &dest) const { |
||||||
|
script->clear(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator()(const CKeyID &keyID) const { |
||||||
|
script->clear(); |
||||||
|
*script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool operator()(const CScriptID &scriptID) const { |
||||||
|
script->clear(); |
||||||
|
*script << OP_HASH160 << scriptID << OP_EQUAL; |
||||||
|
return true; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void CScript::SetDestination(const CTxDestination& dest) |
||||||
|
{ |
||||||
|
boost::apply_visitor(CScriptVisitor(this), dest); |
||||||
|
} |
||||||
|
|
||||||
|
void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys) |
||||||
|
{ |
||||||
|
this->clear(); |
||||||
|
|
||||||
|
*this << EncodeOP_N(nRequired); |
||||||
|
BOOST_FOREACH(const CPubKey& key, keys) |
||||||
|
*this << key; |
||||||
|
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; |
||||||
|
} |
@ -0,0 +1,260 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "script/sign.h" |
||||||
|
|
||||||
|
#include "core.h" |
||||||
|
#include "key.h" |
||||||
|
#include "keystore.h" |
||||||
|
#include "script/standard.h" |
||||||
|
#include "uint256.h" |
||||||
|
|
||||||
|
#include <boost/foreach.hpp> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
typedef vector<unsigned char> valtype; |
||||||
|
|
||||||
|
bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) |
||||||
|
{ |
||||||
|
CKey key; |
||||||
|
if (!keystore.GetKey(address, key)) |
||||||
|
return false; |
||||||
|
|
||||||
|
vector<unsigned char> vchSig; |
||||||
|
if (!key.Sign(hash, vchSig)) |
||||||
|
return false; |
||||||
|
vchSig.push_back((unsigned char)nHashType); |
||||||
|
scriptSigRet << vchSig; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) |
||||||
|
{ |
||||||
|
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(keyID, keystore, hash, nHashType, scriptSigRet)) |
||||||
|
++nSigned; |
||||||
|
} |
||||||
|
return nSigned==nRequired; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
|
||||||
|
// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
|
||||||
|
// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
|
||||||
|
// Returns false if scriptPubKey could not be completely satisfied.
|
||||||
|
//
|
||||||
|
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, |
||||||
|
CScript& scriptSigRet, txnouttype& whichTypeRet) |
||||||
|
{ |
||||||
|
scriptSigRet.clear(); |
||||||
|
|
||||||
|
vector<valtype> vSolutions; |
||||||
|
if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) |
||||||
|
return false; |
||||||
|
|
||||||
|
CKeyID keyID; |
||||||
|
switch (whichTypeRet) |
||||||
|
{ |
||||||
|
case TX_NONSTANDARD: |
||||||
|
case TX_NULL_DATA: |
||||||
|
return false; |
||||||
|
case TX_PUBKEY: |
||||||
|
keyID = CPubKey(vSolutions[0]).GetID(); |
||||||
|
return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); |
||||||
|
case TX_PUBKEYHASH: |
||||||
|
keyID = CKeyID(uint160(vSolutions[0])); |
||||||
|
if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) |
||||||
|
return false; |
||||||
|
else |
||||||
|
{ |
||||||
|
CPubKey vch; |
||||||
|
keystore.GetPubKey(keyID, vch); |
||||||
|
scriptSigRet << vch; |
||||||
|
} |
||||||
|
return true; |
||||||
|
case TX_SCRIPTHASH: |
||||||
|
return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); |
||||||
|
|
||||||
|
case TX_MULTISIG: |
||||||
|
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
|
||||||
|
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) |
||||||
|
{ |
||||||
|
assert(nIn < txTo.vin.size()); |
||||||
|
CTxIn& txin = txTo.vin[nIn]; |
||||||
|
|
||||||
|
// Leave out the signature from the hash, since a signature can't sign itself.
|
||||||
|
// The checksig op will also drop the signatures from its hash.
|
||||||
|
uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); |
||||||
|
|
||||||
|
txnouttype whichType; |
||||||
|
if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (whichType == TX_SCRIPTHASH) |
||||||
|
{ |
||||||
|
// Solver returns the subscript that need to be evaluated;
|
||||||
|
// the final scriptSig is the signatures from that
|
||||||
|
// and then the serialized subscript:
|
||||||
|
CScript subscript = txin.scriptSig; |
||||||
|
|
||||||
|
// Recompute txn hash using subscript in place of scriptPubKey:
|
||||||
|
uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); |
||||||
|
|
||||||
|
txnouttype subType; |
||||||
|
bool fSolved = |
||||||
|
Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; |
||||||
|
// Append serialized subscript whether or not it is completely signed:
|
||||||
|
txin.scriptSig << static_cast<valtype>(subscript); |
||||||
|
if (!fSolved) return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Test solution
|
||||||
|
return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS, 0); |
||||||
|
} |
||||||
|
|
||||||
|
bool SignSignature(const CKeyStore &keystore, 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(keystore, txout.scriptPubKey, txTo, nIn, nHashType); |
||||||
|
} |
||||||
|
|
||||||
|
static CScript PushAll(const vector<valtype>& values) |
||||||
|
{ |
||||||
|
CScript result; |
||||||
|
BOOST_FOREACH(const valtype& v, values) |
||||||
|
result << v; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
static CScript CombineMultisig(CScript scriptPubKey, const CMutableTransaction& txTo, unsigned int nIn, |
||||||
|
const vector<valtype>& vSolutions, |
||||||
|
vector<valtype>& sigs1, vector<valtype>& sigs2) |
||||||
|
{ |
||||||
|
// Combine all the signatures we've got:
|
||||||
|
set<valtype> allsigs; |
||||||
|
BOOST_FOREACH(const valtype& v, sigs1) |
||||||
|
{ |
||||||
|
if (!v.empty()) |
||||||
|
allsigs.insert(v); |
||||||
|
} |
||||||
|
BOOST_FOREACH(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; |
||||||
|
map<valtype, valtype> sigs; |
||||||
|
BOOST_FOREACH(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 (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0)) |
||||||
|
{ |
||||||
|
sigs[pubkey] = sig; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// Now build a merged CScript:
|
||||||
|
unsigned int nSigsHave = 0; |
||||||
|
CScript result; result << OP_0; // pop-one-too-many workaround
|
||||||
|
for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) |
||||||
|
{ |
||||||
|
if (sigs.count(vSolutions[i+1])) |
||||||
|
{ |
||||||
|
result << sigs[vSolutions[i+1]]; |
||||||
|
++nSigsHave; |
||||||
|
} |
||||||
|
} |
||||||
|
// Fill any missing with OP_0:
|
||||||
|
for (unsigned int i = nSigsHave; i < nSigsRequired; i++) |
||||||
|
result << OP_0; |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, |
||||||
|
const txnouttype txType, const vector<valtype>& vSolutions, |
||||||
|
vector<valtype>& sigs1, vector<valtype>& sigs2) |
||||||
|
{ |
||||||
|
switch (txType) |
||||||
|
{ |
||||||
|
case TX_NONSTANDARD: |
||||||
|
case TX_NULL_DATA: |
||||||
|
// Don't know anything about this, assume bigger one is correct:
|
||||||
|
if (sigs1.size() >= sigs2.size()) |
||||||
|
return PushAll(sigs1); |
||||||
|
return PushAll(sigs2); |
||||||
|
case TX_PUBKEY: |
||||||
|
case TX_PUBKEYHASH: |
||||||
|
// Signatures are bigger than placeholders or empty scripts:
|
||||||
|
if (sigs1.empty() || sigs1[0].empty()) |
||||||
|
return PushAll(sigs2); |
||||||
|
return PushAll(sigs1); |
||||||
|
case TX_SCRIPTHASH: |
||||||
|
if (sigs1.empty() || sigs1.back().empty()) |
||||||
|
return PushAll(sigs2); |
||||||
|
else if (sigs2.empty() || sigs2.back().empty()) |
||||||
|
return PushAll(sigs1); |
||||||
|
else |
||||||
|
{ |
||||||
|
// Recur to combine:
|
||||||
|
valtype spk = sigs1.back(); |
||||||
|
CScript pubKey2(spk.begin(), spk.end()); |
||||||
|
|
||||||
|
txnouttype txType2; |
||||||
|
vector<vector<unsigned char> > vSolutions2; |
||||||
|
Solver(pubKey2, txType2, vSolutions2); |
||||||
|
sigs1.pop_back(); |
||||||
|
sigs2.pop_back(); |
||||||
|
CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); |
||||||
|
result << spk; |
||||||
|
return result; |
||||||
|
} |
||||||
|
case TX_MULTISIG: |
||||||
|
return CombineMultisig(scriptPubKey, txTo, nIn, vSolutions, sigs1, sigs2); |
||||||
|
} |
||||||
|
|
||||||
|
return CScript(); |
||||||
|
} |
||||||
|
|
||||||
|
CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, |
||||||
|
const CScript& scriptSig1, const CScript& scriptSig2) |
||||||
|
{ |
||||||
|
txnouttype txType; |
||||||
|
vector<vector<unsigned char> > vSolutions; |
||||||
|
Solver(scriptPubKey, txType, vSolutions); |
||||||
|
|
||||||
|
vector<valtype> stack1; |
||||||
|
EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); |
||||||
|
vector<valtype> stack2; |
||||||
|
EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC, 0); |
||||||
|
|
||||||
|
return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_SCRIPT_SIGN |
||||||
|
#define H_BITCOIN_SCRIPT_SIGN |
||||||
|
|
||||||
|
#include "script/interpreter.h" |
||||||
|
|
||||||
|
class CKeyStore; |
||||||
|
class CScript; |
||||||
|
class CTransaction; |
||||||
|
struct CMutableTransaction; |
||||||
|
|
||||||
|
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); |
||||||
|
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); |
||||||
|
|
||||||
|
// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
|
||||||
|
// combine them intelligently and return the result.
|
||||||
|
CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,254 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "script/standard.h" |
||||||
|
|
||||||
|
#include "script/script.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
#include <boost/foreach.hpp> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
typedef vector<unsigned char> valtype; |
||||||
|
|
||||||
|
const char* GetTxnOutputType(txnouttype t) |
||||||
|
{ |
||||||
|
switch (t) |
||||||
|
{ |
||||||
|
case TX_NONSTANDARD: return "nonstandard"; |
||||||
|
case TX_PUBKEY: return "pubkey"; |
||||||
|
case TX_PUBKEYHASH: return "pubkeyhash"; |
||||||
|
case TX_SCRIPTHASH: return "scripthash"; |
||||||
|
case TX_MULTISIG: return "multisig"; |
||||||
|
case TX_NULL_DATA: return "nulldata"; |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
|
||||||
|
//
|
||||||
|
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet) |
||||||
|
{ |
||||||
|
// Templates
|
||||||
|
static multimap<txnouttype, CScript> mTemplates; |
||||||
|
if (mTemplates.empty()) |
||||||
|
{ |
||||||
|
// Standard tx, sender provides pubkey, receiver adds signature
|
||||||
|
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); |
||||||
|
|
||||||
|
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
|
||||||
|
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); |
||||||
|
|
||||||
|
// Sender provides N pubkeys, receivers provides M signatures
|
||||||
|
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); |
||||||
|
|
||||||
|
// Empty, provably prunable, data-carrying output
|
||||||
|
if (GetBoolArg("-datacarrier", true)) |
||||||
|
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); |
||||||
|
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); |
||||||
|
} |
||||||
|
|
||||||
|
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
|
||||||
|
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
|
||||||
|
if (scriptPubKey.IsPayToScriptHash()) |
||||||
|
{ |
||||||
|
typeRet = TX_SCRIPTHASH; |
||||||
|
vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); |
||||||
|
vSolutionsRet.push_back(hashBytes); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Scan templates
|
||||||
|
const CScript& script1 = scriptPubKey; |
||||||
|
BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) |
||||||
|
{ |
||||||
|
const CScript& script2 = tplate.second; |
||||||
|
vSolutionsRet.clear(); |
||||||
|
|
||||||
|
opcodetype opcode1, opcode2; |
||||||
|
vector<unsigned char> vch1, vch2; |
||||||
|
|
||||||
|
// 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 (vch1.size() >= 33 && vch1.size() <= 65) |
||||||
|
{ |
||||||
|
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 (vch1.size() < 33 || vch1.size() > 65) |
||||||
|
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 (opcode2 == OP_SMALLDATA) |
||||||
|
{ |
||||||
|
// small pushdata, <= MAX_OP_RETURN_RELAY bytes
|
||||||
|
if (vch1.size() > MAX_OP_RETURN_RELAY) |
||||||
|
break; |
||||||
|
} |
||||||
|
else if (opcode1 != opcode2 || vch1 != vch2) |
||||||
|
{ |
||||||
|
// Others must match exactly
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
vSolutionsRet.clear(); |
||||||
|
typeRet = TX_NONSTANDARD; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions) |
||||||
|
{ |
||||||
|
switch (t) |
||||||
|
{ |
||||||
|
case TX_NONSTANDARD: |
||||||
|
case TX_NULL_DATA: |
||||||
|
return -1; |
||||||
|
case TX_PUBKEY: |
||||||
|
return 1; |
||||||
|
case TX_PUBKEYHASH: |
||||||
|
return 2; |
||||||
|
case TX_MULTISIG: |
||||||
|
if (vSolutions.size() < 1 || vSolutions[0].size() < 1) |
||||||
|
return -1; |
||||||
|
return vSolutions[0][0] + 1; |
||||||
|
case TX_SCRIPTHASH: |
||||||
|
return 1; // doesn't include args needed by the script
|
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) |
||||||
|
{ |
||||||
|
vector<valtype> vSolutions; |
||||||
|
if (!Solver(scriptPubKey, whichType, vSolutions)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (whichType == TX_MULTISIG) |
||||||
|
{ |
||||||
|
unsigned char m = vSolutions.front()[0]; |
||||||
|
unsigned char n = vSolutions.back()[0]; |
||||||
|
// Support up to x-of-3 multisig txns as standard
|
||||||
|
if (n < 1 || n > 3) |
||||||
|
return false; |
||||||
|
if (m < 1 || m > n) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return whichType != TX_NONSTANDARD; |
||||||
|
} |
||||||
|
|
||||||
|
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) |
||||||
|
{ |
||||||
|
vector<valtype> vSolutions; |
||||||
|
txnouttype whichType; |
||||||
|
if (!Solver(scriptPubKey, whichType, vSolutions)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (whichType == TX_PUBKEY) |
||||||
|
{ |
||||||
|
addressRet = CPubKey(vSolutions[0]).GetID(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
else if (whichType == TX_PUBKEYHASH) |
||||||
|
{ |
||||||
|
addressRet = CKeyID(uint160(vSolutions[0])); |
||||||
|
return true; |
||||||
|
} |
||||||
|
else if (whichType == TX_SCRIPTHASH) |
||||||
|
{ |
||||||
|
addressRet = CScriptID(uint160(vSolutions[0])); |
||||||
|
return true; |
||||||
|
} |
||||||
|
// Multisig txns have more than one address...
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet) |
||||||
|
{ |
||||||
|
addressRet.clear(); |
||||||
|
typeRet = TX_NONSTANDARD; |
||||||
|
vector<valtype> vSolutions; |
||||||
|
if (!Solver(scriptPubKey, typeRet, vSolutions)) |
||||||
|
return false; |
||||||
|
if (typeRet == TX_NULL_DATA){ |
||||||
|
// This is data, not addresses
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (typeRet == TX_MULTISIG) |
||||||
|
{ |
||||||
|
nRequiredRet = vSolutions.front()[0]; |
||||||
|
for (unsigned int i = 1; i < vSolutions.size()-1; i++) |
||||||
|
{ |
||||||
|
CTxDestination address = CPubKey(vSolutions[i]).GetID(); |
||||||
|
addressRet.push_back(address); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
nRequiredRet = 1; |
||||||
|
CTxDestination address; |
||||||
|
if (!ExtractDestination(scriptPubKey, address)) |
||||||
|
return false; |
||||||
|
addressRet.push_back(address); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_SCRIPT_STANDARD |
||||||
|
#define H_BITCOIN_SCRIPT_STANDARD |
||||||
|
|
||||||
|
#include "script/script.h" |
||||||
|
#include "script/interpreter.h" |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
class CScript; |
||||||
|
|
||||||
|
static const unsigned int MAX_OP_RETURN_RELAY = 40; // bytes
|
||||||
|
|
||||||
|
// Mandatory script verification flags that all new blocks must comply with for
|
||||||
|
// them to be valid. (but old blocks may not comply with) Currently just P2SH,
|
||||||
|
// but in the future other flags may be added, such as a soft-fork to enforce
|
||||||
|
// strict DER encoding.
|
||||||
|
//
|
||||||
|
// Failing one of these tests may trigger a DoS ban - see CheckInputs() for
|
||||||
|
// details.
|
||||||
|
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH; |
||||||
|
|
||||||
|
// Standard script verification flags that standard transactions will comply
|
||||||
|
// with. However scripts violating these flags may still be present in valid
|
||||||
|
// blocks and we must accept those blocks.
|
||||||
|
static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY_FLAGS | |
||||||
|
SCRIPT_VERIFY_STRICTENC | |
||||||
|
SCRIPT_VERIFY_NULLDUMMY; |
||||||
|
|
||||||
|
// For convenience, standard but not mandatory verify flags.
|
||||||
|
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; |
||||||
|
|
||||||
|
enum txnouttype |
||||||
|
{ |
||||||
|
TX_NONSTANDARD, |
||||||
|
// 'standard' transaction types:
|
||||||
|
TX_PUBKEY, |
||||||
|
TX_PUBKEYHASH, |
||||||
|
TX_SCRIPTHASH, |
||||||
|
TX_MULTISIG, |
||||||
|
TX_NULL_DATA, |
||||||
|
}; |
||||||
|
|
||||||
|
const char* GetTxnOutputType(txnouttype t); |
||||||
|
|
||||||
|
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); |
||||||
|
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); |
||||||
|
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); |
||||||
|
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); |
||||||
|
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,127 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "scriptutils.h" |
||||||
|
|
||||||
|
#include "key.h" |
||||||
|
#include "keystore.h" |
||||||
|
#include "script/standard.h" |
||||||
|
|
||||||
|
#include <boost/foreach.hpp> |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
typedef vector<unsigned char> valtype; |
||||||
|
|
||||||
|
unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore) |
||||||
|
{ |
||||||
|
unsigned int nResult = 0; |
||||||
|
BOOST_FOREACH(const valtype& pubkey, pubkeys) |
||||||
|
{ |
||||||
|
CKeyID keyID = CPubKey(pubkey).GetID(); |
||||||
|
if (keystore.HaveKey(keyID)) |
||||||
|
++nResult; |
||||||
|
} |
||||||
|
return nResult; |
||||||
|
} |
||||||
|
|
||||||
|
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) |
||||||
|
{ |
||||||
|
CScript script; |
||||||
|
script.SetDestination(dest); |
||||||
|
return IsMine(keystore, script); |
||||||
|
} |
||||||
|
|
||||||
|
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) |
||||||
|
{ |
||||||
|
vector<valtype> vSolutions; |
||||||
|
txnouttype whichType; |
||||||
|
if (!Solver(scriptPubKey, whichType, vSolutions)) { |
||||||
|
if (keystore.HaveWatchOnly(scriptPubKey)) |
||||||
|
return ISMINE_WATCH_ONLY; |
||||||
|
return ISMINE_NO; |
||||||
|
} |
||||||
|
|
||||||
|
CKeyID keyID; |
||||||
|
switch (whichType) |
||||||
|
{ |
||||||
|
case TX_NONSTANDARD: |
||||||
|
case TX_NULL_DATA: |
||||||
|
break; |
||||||
|
case TX_PUBKEY: |
||||||
|
keyID = CPubKey(vSolutions[0]).GetID(); |
||||||
|
if (keystore.HaveKey(keyID)) |
||||||
|
return ISMINE_SPENDABLE; |
||||||
|
break; |
||||||
|
case TX_PUBKEYHASH: |
||||||
|
keyID = CKeyID(uint160(vSolutions[0])); |
||||||
|
if (keystore.HaveKey(keyID)) |
||||||
|
return ISMINE_SPENDABLE; |
||||||
|
break; |
||||||
|
case TX_SCRIPTHASH: |
||||||
|
{ |
||||||
|
CScriptID scriptID = CScriptID(uint160(vSolutions[0])); |
||||||
|
CScript subscript; |
||||||
|
if (keystore.GetCScript(scriptID, subscript)) { |
||||||
|
isminetype ret = IsMine(keystore, subscript); |
||||||
|
if (ret == ISMINE_SPENDABLE) |
||||||
|
return ret; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case TX_MULTISIG: |
||||||
|
{ |
||||||
|
// Only consider transactions "mine" if we own ALL the
|
||||||
|
// keys involved. multi-signature transactions that are
|
||||||
|
// partially owned (somebody else has a key that can spend
|
||||||
|
// them) enable spend-out-from-under-you attacks, especially
|
||||||
|
// in shared-wallet situations.
|
||||||
|
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); |
||||||
|
if (HaveKeys(keys, keystore) == keys.size()) |
||||||
|
return ISMINE_SPENDABLE; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (keystore.HaveWatchOnly(scriptPubKey)) |
||||||
|
return ISMINE_WATCH_ONLY; |
||||||
|
return ISMINE_NO; |
||||||
|
} |
||||||
|
|
||||||
|
class CAffectedKeysVisitor : public boost::static_visitor<void> { |
||||||
|
private: |
||||||
|
const CKeyStore &keystore; |
||||||
|
std::vector<CKeyID> &vKeys; |
||||||
|
|
||||||
|
public: |
||||||
|
CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} |
||||||
|
|
||||||
|
void Process(const CScript &script) { |
||||||
|
txnouttype type; |
||||||
|
std::vector<CTxDestination> vDest; |
||||||
|
int nRequired; |
||||||
|
if (ExtractDestinations(script, type, vDest, nRequired)) { |
||||||
|
BOOST_FOREACH(const CTxDestination &dest, vDest) |
||||||
|
boost::apply_visitor(*this, dest); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void operator()(const CKeyID &keyId) { |
||||||
|
if (keystore.HaveKey(keyId)) |
||||||
|
vKeys.push_back(keyId); |
||||||
|
} |
||||||
|
|
||||||
|
void operator()(const CScriptID &scriptId) { |
||||||
|
CScript script; |
||||||
|
if (keystore.GetCScript(scriptId, script)) |
||||||
|
Process(script); |
||||||
|
} |
||||||
|
|
||||||
|
void operator()(const CNoDestination &none) {} |
||||||
|
}; |
||||||
|
|
||||||
|
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys) { |
||||||
|
CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||||
|
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef H_BITCOIN_SCRIPTUTILS |
||||||
|
#define H_BITCOIN_SCRIPTUTILS |
||||||
|
|
||||||
|
#include "key.h" |
||||||
|
#include "script/script.h" |
||||||
|
|
||||||
|
class CKeyStore; |
||||||
|
|
||||||
|
/** IsMine() return codes */ |
||||||
|
enum isminetype |
||||||
|
{ |
||||||
|
ISMINE_NO = 0, |
||||||
|
ISMINE_WATCH_ONLY = 1, |
||||||
|
ISMINE_SPENDABLE = 2, |
||||||
|
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE |
||||||
|
}; |
||||||
|
/** used for bitflags of isminetype */ |
||||||
|
typedef uint8_t isminefilter; |
||||||
|
|
||||||
|
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); |
||||||
|
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); |
||||||
|
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); |
||||||
|
|
||||||
|
#endif // H_BITCOIN_SCRIPT
|
Loading…
Reference in new issue