Browse Source
pull/145/heade088d65
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@gmail.com](/assets/img/avatar_default.png)
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
@ -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