Browse Source

Merge branch 'op_eval'

0.8
Gavin Andresen 13 years ago
parent
commit
595925592d
  1. 48
      src/base58.h
  2. 104
      src/bitcoinrpc.cpp
  3. 1
      src/crypter.h
  4. 9
      src/db.cpp
  5. 27
      src/db.h
  6. 33
      src/keystore.cpp
  7. 11
      src/keystore.h
  8. 209
      src/main.cpp
  9. 38
      src/main.h
  10. 5
      src/makefile.osx
  11. 6
      src/makefile.unix
  12. 577
      src/script.cpp
  13. 243
      src/script.h
  14. 7
      src/test/DoS_tests.cpp
  15. 4
      src/test/base58_tests.cpp
  16. 12
      src/test/base64_tests.cpp
  17. 2
      src/test/miner_tests.cpp
  18. 294
      src/test/multisig_tests.cpp
  19. 234
      src/test/script_op_eval_tests.cpp
  20. 46
      src/test/script_tests.cpp
  21. 23
      src/test/test_bitcoin.cpp
  22. 4
      src/test/transaction_tests.cpp
  23. 2
      src/test/uint160_tests.cpp
  24. 2
      src/test/uint256_tests.cpp
  25. 4
      src/test/util_tests.cpp
  26. 37
      src/wallet.cpp
  27. 13
      src/wallet.h

48
src/base58.h

@ -253,14 +253,26 @@ public:
}; };
// base58-encoded bitcoin addresses // base58-encoded bitcoin addresses
// Addresses have version 0 or 111 (testnet) // Public-key-hash-addresses have version 0 (or 192 testnet)
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key
// Script-hash-addresses (OP_EVAL) have version 5 (or 196 testnet)
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script
class CBitcoinAddress : public CBase58Data class CBitcoinAddress : public CBase58Data
{ {
public: public:
void SetHash160(const uint160& hash160) enum
{ {
SetData(fTestNet ? 111 : 0, &hash160, 20); PUBKEY_ADDRESS = 0,
SCRIPT_ADDRESS = 5,
PUBKEY_ADDRESS_TEST = 192,
PUBKEY_ADDRESS_TEST_LEGACY = 111, // Deprecated: old testnet address
SCRIPT_ADDRESS_TEST = 196,
};
bool SetHash160(const uint160& hash160)
{
SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &hash160, 20);
return true;
} }
void SetPubKey(const std::vector<unsigned char>& vchPubKey) void SetPubKey(const std::vector<unsigned char>& vchPubKey)
@ -268,16 +280,34 @@ public:
SetHash160(Hash160(vchPubKey)); SetHash160(Hash160(vchPubKey));
} }
bool SetScriptHash160(const uint160& hash160)
{
SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &hash160, 20);
return true;
}
bool IsValid() const bool IsValid() const
{ {
int nExpectedSize = 20; int nExpectedSize = 20;
bool fExpectTestNet = false; bool fExpectTestNet = false;
switch(nVersion) switch(nVersion)
{ {
case 0: case PUBKEY_ADDRESS:
nExpectedSize = 20; // Hash of public key
fExpectTestNet = false;
break;
case SCRIPT_ADDRESS:
nExpectedSize = 20; // OP_EVAL, hash of CScript
fExpectTestNet = false;
break; break;
case 111: case PUBKEY_ADDRESS_TEST_LEGACY:
case PUBKEY_ADDRESS_TEST:
nExpectedSize = 20;
fExpectTestNet = true;
break;
case SCRIPT_ADDRESS_TEST:
nExpectedSize = 20;
fExpectTestNet = true; fExpectTestNet = true;
break; break;
@ -286,6 +316,14 @@ public:
} }
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
} }
bool IsScript() const
{
if (!IsValid())
return false;
if (fTestNet)
return nVersion == SCRIPT_ADDRESS_TEST;
return nVersion == SCRIPT_ADDRESS;
}
CBitcoinAddress() CBitcoinAddress()
{ {

104
src/bitcoinrpc.cpp

@ -673,7 +673,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
if (params.size() > 1) if (params.size() > 1)
nMinDepth = params[1].get_int(); nMinDepth = params[1].get_int();
// Get the set of pub keys that have the label // Get the set of pub keys assigned to account
string strAccount = AccountFromValue(params[0]); string strAccount = AccountFromValue(params[0]);
set<CBitcoinAddress> setAddress; set<CBitcoinAddress> setAddress;
GetAccountAddresses(strAccount, setAddress); GetAccountAddresses(strAccount, setAddress);
@ -942,6 +942,68 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex(); return wtx.GetHash().GetHex();
} }
Value addmultisigaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
"Add a nrequired-to-sign multisignature address to the wallet\"\n"
"each key is a bitcoin address, hex or base58 public key\n"
"If [account] is specified, assign address to [account].";
throw runtime_error(msg);
}
if (!fTestNet)
throw runtime_error("addmultisigaddress available only when running -testnet\n");
int nRequired = params[0].get_int();
const Array& keys = params[1].get_array();
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);
// Gather public keys
if (keys.size() < nRequired)
throw runtime_error(
strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
std::vector<CKey> pubkeys;
pubkeys.resize(keys.size());
for (int i = 0; i < keys.size(); i++)
{
const std::string& ks = keys[i].get_str();
if (ks.size() == 130) // hex public key
pubkeys[i].SetPubKey(ParseHex(ks));
else if (ks.size() > 34) // base58-encoded
{
std::vector<unsigned char> vchPubKey;
if (DecodeBase58(ks, vchPubKey))
pubkeys[i].SetPubKey(vchPubKey);
else
throw runtime_error("Error base58 decoding key: "+ks);
}
else // bitcoin address for key in this wallet
{
CBitcoinAddress address(ks);
if (!pwalletMain->GetKey(address, pubkeys[i]))
throw runtime_error(
strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
}
}
// Construct using OP_EVAL
CScript inner;
inner.SetMultisig(nRequired, pubkeys);
uint160 scriptHash = Hash160(inner);
CScript scriptPubKey;
scriptPubKey.SetEval(inner);
pwalletMain->AddCScript(scriptHash, inner);
CBitcoinAddress address;
address.SetScriptHash160(scriptHash);
pwalletMain->SetAddressBookName(address, strAccount);
return address.ToString();
}
struct tallyitem struct tallyitem
{ {
@ -1602,7 +1664,35 @@ Value validateaddress(const Array& params, bool fHelp)
// version of the address: // version of the address:
string currentAddress = address.ToString(); string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress)); ret.push_back(Pair("address", currentAddress));
ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0))); if (pwalletMain->HaveKey(address))
{
ret.push_back(Pair("ismine", true));
std::vector<unsigned char> vchPubKey;
pwalletMain->GetPubKey(address, vchPubKey);
ret.push_back(Pair("pubkey", HexStr(vchPubKey)));
std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey)));
}
else if (pwalletMain->HaveCScript(address.GetHash160()))
{
ret.push_back(Pair("isscript", true));
CScript subscript;
pwalletMain->GetCScript(address.GetHash160(), subscript);
ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
std::vector<CBitcoinAddress> addresses;
txnouttype whichType;
int nRequired;
ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
ret.push_back(Pair("script", GetTxnOutputType(whichType)));
Array a;
BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
a.push_back(addr.ToString());
ret.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
ret.push_back(Pair("sigsrequired", nRequired));
}
else
ret.push_back(Pair("ismine", false));
if (pwalletMain->mapAddressBook.count(address)) if (pwalletMain->mapAddressBook.count(address))
ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
} }
@ -1847,6 +1937,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd), make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom), make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany), make_pair("sendmany", &sendmany),
make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction), make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions), make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage), make_pair("signmessage", &signmessage),
@ -2490,6 +2581,15 @@ int CommandLineRPC(int argc, char *argv[])
params[1] = v.get_obj(); params[1] = v.get_obj();
} }
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1)
{
string s = params[1].get_str();
Value v;
if (!read_string(s, v) || v.type() != array_type)
throw runtime_error("addmultisigaddress: type mismatch "+s);
params[1] = v.get_array();
}
// Execute // Execute
Object reply = CallRPC(strMethod, params); Object reply = CallRPC(strMethod, params);

1
src/crypter.h

@ -4,6 +4,7 @@
#ifndef __CRYPTER_H__ #ifndef __CRYPTER_H__
#define __CRYPTER_H__ #define __CRYPTER_H__
#include "util.h" /* for SecureString */
#include "key.h" #include "key.h"
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;

9
src/db.cpp

@ -934,6 +934,15 @@ int CWalletDB::LoadWallet(CWallet* pwallet)
if (nMinVersion > CLIENT_VERSION) if (nMinVersion > CLIENT_VERSION)
return DB_TOO_NEW; return DB_TOO_NEW;
} }
else if (strType == "cscript")
{
uint160 hash;
ssKey >> hash;
CScript script;
ssValue >> script;
if (!pwallet->LoadCScript(hash, script))
return DB_CORRUPT;
}
} }
pcursor->close(); pcursor->close();
} }

27
src/db.h

@ -13,17 +13,17 @@
#include <db_cxx.h> #include <db_cxx.h>
class CTxIndex; class CAccount;
class CAccountingEntry;
class CAddress;
class CBlockLocator;
class CDiskBlockIndex; class CDiskBlockIndex;
class CDiskTxPos; class CDiskTxPos;
class CMasterKey;
class COutPoint; class COutPoint;
class CAddress; class CTxIndex;
class CWalletTx;
class CWallet; class CWallet;
class CAccount; class CWalletTx;
class CAccountingEntry;
class CBlockLocator;
extern unsigned int nWalletDBUpdated; extern unsigned int nWalletDBUpdated;
extern DbEnv dbenv; extern DbEnv dbenv;
@ -420,6 +420,19 @@ public:
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
} }
// Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013
bool ReadCScript(const uint160 &hash, CScript& redeemScript)
{
redeemScript.clear();
return Read(std::make_pair(std::string("cscript"), hash), redeemScript);
}
bool WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
bool WriteBestBlock(const CBlockLocator& locator) bool WriteBestBlock(const CBlockLocator& locator)
{ {
nWalletDBUpdated++; nWalletDBUpdated++;

33
src/keystore.cpp

@ -4,8 +4,9 @@
// file license.txt or http://www.opensource.org/licenses/mit-license.php. // file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h" #include "headers.h"
#include "db.h"
#include "crypter.h" #include "crypter.h"
#include "db.h"
#include "script.h"
std::vector<unsigned char> CKeyStore::GenerateNewKey() std::vector<unsigned char> CKeyStore::GenerateNewKey()
{ {
@ -33,6 +34,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
return true; return true;
} }
bool CBasicKeyStore::AddCScript(const uint160 &hash, const CScript& redeemScript)
{
CRITICAL_BLOCK(cs_KeyStore)
mapScripts[hash] = redeemScript;
return true;
}
bool CBasicKeyStore::HaveCScript(const uint160& hash) const
{
bool result;
CRITICAL_BLOCK(cs_KeyStore)
result = (mapScripts.count(hash) > 0);
return result;
}
bool CBasicKeyStore::GetCScript(const uint160 &hash, CScript& redeemScriptOut) const
{
CRITICAL_BLOCK(cs_KeyStore)
{
ScriptMap::const_iterator mi = mapScripts.find(hash);
if (mi != mapScripts.end())
{
redeemScriptOut = (*mi).second;
return true;
}
}
return false;
}
bool CCryptoKeyStore::SetCrypted() bool CCryptoKeyStore::SetCrypted()
{ {
CRITICAL_BLOCK(cs_KeyStore) CRITICAL_BLOCK(cs_KeyStore)

11
src/keystore.h

@ -6,6 +6,7 @@
#define BITCOIN_KEYSTORE_H #define BITCOIN_KEYSTORE_H
#include "crypter.h" #include "crypter.h"
#include "script.h"
// A virtual base class for key stores // A virtual base class for key stores
class CKeyStore class CKeyStore
@ -31,6 +32,11 @@ public:
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0; virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
// Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013
virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript) =0;
virtual bool HaveCScript(const uint160 &hash) const =0;
virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const =0;
// Generate a new key, and add it to the store // Generate a new key, and add it to the store
virtual std::vector<unsigned char> GenerateNewKey(); virtual std::vector<unsigned char> GenerateNewKey();
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
@ -44,12 +50,14 @@ public:
}; };
typedef std::map<CBitcoinAddress, CSecret> KeyMap; typedef std::map<CBitcoinAddress, CSecret> KeyMap;
typedef std::map<uint160, CScript > ScriptMap;
// Basic key store, that keeps keys in an address->secret map // Basic key store, that keeps keys in an address->secret map
class CBasicKeyStore : public CKeyStore class CBasicKeyStore : public CKeyStore
{ {
protected: protected:
KeyMap mapKeys; KeyMap mapKeys;
ScriptMap mapScripts;
public: public:
bool AddKey(const CKey& key); bool AddKey(const CKey& key);
@ -86,6 +94,9 @@ public:
} }
return false; return false;
} }
virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript);
virtual bool HaveCScript(const uint160 &hash) const;
virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const;
}; };
typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap; typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;

209
src/main.cpp

@ -246,6 +246,67 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
return ReadFromDisk(txdb, prevout, txindex); return ReadFromDisk(txdb, prevout, txindex);
} }
bool CTransaction::IsStandard() const
{
BOOST_FOREACH(const CTxIn& txin, vin)
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// in an OP_EVAL, which is 3 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
if (txin.scriptSig.size() > 500)
return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size());
if (!txin.scriptSig.IsPushOnly())
return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str());
}
BOOST_FOREACH(const CTxOut& txout, vout)
if (!::IsStandard(txout.scriptPubKey))
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
return true;
}
//
// Check transaction inputs, and make sure any
// OP_EVAL transactions are evaluating IsStandard scripts
//
// Why bother? To avoid denial-of-service attacks; an attacker
// can submit a standard DUP HASH... OP_EVAL transaction,
// which will get accepted into blocks. The script being
// EVAL'ed can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
bool CTransaction::AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
{
if (fTestNet)
return true; // Allow non-standard on testnet
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
assert(mapInputs.count(prevout.hash) > 0);
CTransaction& txPrev = mapInputs[prevout.hash].second;
vector<vector<unsigned char> > vSolutions;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:
CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey;
if (!Solver(prevScript, whichType, vSolutions))
return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str());
if (whichType == TX_SCRIPTHASH)
{
vector<vector<unsigned char> > stack;
int nUnused;
if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused))
return false;
CScript subscript(stack.back().begin(), stack.back().end());
if (!::IsStandard(subscript))
return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str());
}
}
return true;
}
int CMerkleTx::SetMerkleBranch(const CBlock* pblock) int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@ -369,15 +430,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ((int64)nLockTime > std::numeric_limits<int>::max()) if ((int64)nLockTime > std::numeric_limits<int>::max())
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
// Safety limits
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 34 bytes.
// 34 bytes because a TxOut is:
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
if (GetSigOpCount() > nSize / 34 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
// Rather not work on nonstandard transactions (unless -testnet) // Rather not work on nonstandard transactions (unless -testnet)
if (!fTestNet && !IsStandard()) if (!fTestNet && !IsStandard())
return error("AcceptToMemoryPool() : nonstandard transaction type"); return error("AcceptToMemoryPool() : nonstandard transaction type");
@ -421,15 +473,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if (fCheckInputs) if (fCheckInputs)
{ {
// Check against previous transactions map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
map<uint256, CTxIndex> mapUnused; map<uint256, CTxIndex> mapUnused;
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
{
if (pfMissingInputs)
*pfMissingInputs = true;
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
}
// Check for non-standard OP_EVALs in inputs
if (!AreInputsStandard(mapInputs))
return error("AcceptToMemoryPool() : nonstandard transaction input");
// Check against previous transactions
int64 nFees = 0; int64 nFees = 0;
if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) int nSigOps = 0;
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
{ {
if (pfMissingInputs) if (pfMissingInputs)
*pfMissingInputs = true; *pfMissingInputs = true;
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
} }
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 65 bytes.
// 65 bytes because that is the minimum size of an ECDSA signature
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
if (nSigOps > nSize / 65 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
// Don't accept it if it can't get into a block // Don't accept it if it can't get into a block
if (nFees < GetMinFee(1000, true, true)) if (nFees < GetMinFee(1000, true, true))
@ -826,27 +897,25 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
} }
bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx, bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
if (!IsCoinBase())
{ {
int64 nValueIn = 0; if (IsCoinBase())
return true; // Coinbase transactions have no inputs to fetch.
for (int i = 0; i < vin.size(); i++) for (int i = 0; i < vin.size(); i++)
{ {
COutPoint prevout = vin[i].prevout; COutPoint prevout = vin[i].prevout;
if (inputsRet.count(prevout.hash))
continue; // Got it already
// Read txindex // Read txindex
CTxIndex txindex; CTxIndex& txindex = inputsRet[prevout.hash].first;
bool fFound = true; bool fFound = true;
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
{ {
// Get txindex from current proposed changes // Get txindex from current proposed changes
txindex = mapTestPool[prevout.hash]; txindex = mapTestPool.find(prevout.hash)->second;
} }
else else
{ {
@ -854,17 +923,17 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
fFound = txdb.ReadTxIndex(prevout.hash, txindex); fFound = txdb.ReadTxIndex(prevout.hash, txindex);
} }
if (!fFound && (fBlock || fMiner)) if (!fFound && (fBlock || fMiner))
return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
// Read txPrev // Read txPrev
CTransaction txPrev; CTransaction& txPrev = inputsRet[prevout.hash].second;
if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
{ {
// Get prev tx from single transactions in memory // Get prev tx from single transactions in memory
CRITICAL_BLOCK(cs_mapTransactions) CRITICAL_BLOCK(cs_mapTransactions)
{ {
if (!mapTransactions.count(prevout.hash)) if (!mapTransactions.count(prevout.hash))
return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
txPrev = mapTransactions[prevout.hash]; txPrev = mapTransactions[prevout.hash];
} }
if (!fFound) if (!fFound)
@ -874,8 +943,29 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
{ {
// Get prev tx from disk // Get prev tx from disk
if (!txPrev.ReadFromDisk(txindex.pos)) if (!txPrev.ReadFromDisk(txindex.pos))
return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
} }
}
return true;
}
bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
if (!IsCoinBase())
{
int64 nValueIn = 0;
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
assert(inputs.count(prevout.hash) > 0);
CTxIndex& txindex = inputs[prevout.hash].first;
CTransaction& txPrev = inputs[prevout.hash].second;
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
@ -890,9 +980,27 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
// (before the last blockchain checkpoint). This is safe because block merkle hashes are // (before the last blockchain checkpoint). This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint. // still computed and checked, and any change will be caught at the next checkpoint.
if (!(fBlock && IsInitialBlockDownload())) if (!(fBlock && IsInitialBlockDownload()))
{
bool fStrictOpEval = true;
// This code should be removed when OP_EVAL has
// a majority of hashing power on the network.
if (fBlock)
{
// To avoid being on the short end of a block-chain split,
// interpret OP_EVAL as a NO_OP until blocks with timestamps
// after opevaltime:
int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012
fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime);
}
// if !fBlock, then always be strict-- don't accept
// invalid-under-new-rules OP_EVAL transactions into
// our memory pool (don't relay them, don't include them
// in blocks we mine).
// Verify signature // Verify signature
if (!VerifySignature(txPrev, *this, i)) if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval))
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
}
// Check for conflicts (double-spend) // Check for conflicts (double-spend)
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
@ -965,7 +1073,8 @@ bool CTransaction::ClientConnectInputs()
return false; return false;
// Verify signature // Verify signature
if (!VerifySignature(txPrev, *this, i)) int nUnused = 0;
if (!VerifySignature(txPrev, *this, i, nUnused, false))
return error("ConnectInputs() : VerifySignature failed"); return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@ -1023,14 +1132,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
map<uint256, CTxIndex> mapQueuedChanges; map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0; int64 nFees = 0;
int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx) BOOST_FOREACH(CTransaction& tx, vtx)
{ {
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
nTxPos += ::GetSerializeSize(tx, SER_DISK); nTxPos += ::GetSerializeSize(tx, SER_DISK);
if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false)) map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
return false;
if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps))
return false; return false;
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
} }
// Write queued txindex changes // Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{ {
@ -1291,8 +1407,21 @@ bool CBlock::CheckBlock() const
if (!tx.CheckTransaction()) if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
// Check that it's not full of nonstandard transactions // This code should be removed when a compatibility-breaking block chain split has passed.
if (GetSigOpCount() > MAX_BLOCK_SIGOPS) // Compatibility check for old clients that counted sigops differently:
int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount();
}
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount();
}
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkleroot // Check merkleroot
@ -2827,9 +2956,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue; continue;
int nTxSigOps = tx.GetSigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Transaction fee required depends on block size // Transaction fee required depends on block size
bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
@ -2838,7 +2964,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Connecting shouldn't fail due to dependency on other memory pool transactions // Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency // because we're already processing them in order of dependency
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool); map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
continue;
int nTxSigOps = 0;
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
continue;
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue; continue;
swap(mapTestPool, mapTestPoolTmp); swap(mapTestPool, mapTestPoolTmp);
@ -2887,6 +3019,13 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
} }
++nExtraNonce; ++nExtraNonce;
pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce);
// Put "OP_EVAL" in the coinbase so everybody can tell when
// a majority of miners support it
const char* pOpEvalName = GetOpName(OP_EVAL);
pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector<unsigned char>(pOpEvalName, pOpEvalName+strlen(pOpEvalName));
assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->hashMerkleRoot = pblock->BuildMerkleTree();
} }

38
src/main.h

@ -493,26 +493,8 @@ public:
return (vin.size() == 1 && vin[0].prevout.IsNull()); return (vin.size() == 1 && vin[0].prevout.IsNull());
} }
int GetSigOpCount() const bool IsStandard() const;
{ bool AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
int n = 0;
BOOST_FOREACH(const CTxIn& txin, vin)
n += txin.scriptSig.GetSigOpCount();
BOOST_FOREACH(const CTxOut& txout, vout)
n += txout.scriptPubKey.GetSigOpCount();
return n;
}
bool IsStandard() const
{
BOOST_FOREACH(const CTxIn& txin, vin)
if (!txin.scriptSig.IsPushOnly())
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
BOOST_FOREACH(const CTxOut& txout, vout)
if (!::IsStandard(txout.scriptPubKey))
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
return true;
}
int64 GetValueOut() const int64 GetValueOut() const
{ {
@ -640,8 +622,13 @@ public:
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
bool ReadFromDisk(COutPoint prevout); bool ReadFromDisk(COutPoint prevout);
bool DisconnectInputs(CTxDB& txdb); bool DisconnectInputs(CTxDB& txdb);
bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); // Fetch from memory and/or disk. inputsRet keys are transaction hashes.
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
bool fBlock, bool fMiner, std::map<uint256, std::pair<CTxIndex, CTransaction> >& inputsRet);
bool ConnectInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > inputs,
std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0);
bool ClientConnectInputs(); bool ClientConnectInputs();
bool CheckTransaction() const; bool CheckTransaction() const;
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
@ -851,13 +838,6 @@ public:
return (int64)nTime; return (int64)nTime;
} }
int GetSigOpCount() const
{
int n = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
n += tx.GetSigOpCount();
return n;
}
uint256 BuildMerkleTree() const uint256 BuildMerkleTree() const

5
src/makefile.osx vendored

@ -10,6 +10,7 @@ CXX=llvm-g++
DEPSDIR=/opt/local DEPSDIR=/opt/local
INCLUDEPATHS= \ INCLUDEPATHS= \
-I"$(CURDIR)" \
-I"$(DEPSDIR)/include" \ -I"$(DEPSDIR)/include" \
-I"$(DEPSDIR)/include/db48" -I"$(DEPSDIR)/include/db48"
@ -112,6 +113,8 @@ obj/nogui/%.o: %.cpp
bitcoind: $(OBJS:obj/%=obj/nogui/%) bitcoind: $(OBJS:obj/%=obj/nogui/%)
$(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS)
TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp))
obj/test/%.o: test/%.cpp obj/test/%.o: test/%.cpp
$(CXX) -c $(CFLAGS) -MMD -o $@ $< $(CXX) -c $(CFLAGS) -MMD -o $@ $<
@cp $(@:%.o=%.d) $(@:%.o=%.P); \ @cp $(@:%.o=%.d) $(@:%.o=%.P); \
@ -119,7 +122,7 @@ obj/test/%.o: test/%.cpp
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
rm -f $(@:%.o=%.d) rm -f $(@:%.o=%.d)
test_bitcoin: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) test_bitcoin: $(TESTOBJS) $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%))
$(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a
clean: clean:

6
src/makefile.unix

@ -6,7 +6,7 @@ USE_UPNP:=0
DEFS=-DNOPCH DEFS=-DNOPCH
DEFS += $(addprefix -I,$(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) DEFS += $(addprefix -I,$(CURDIR) $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH))
LIBS += $(addprefix -l,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) LIBS += $(addprefix -l,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH))
LMODE = dynamic LMODE = dynamic
@ -141,6 +141,8 @@ obj/nogui/%.o: %.cpp
bitcoind: $(OBJS:obj/%=obj/nogui/%) bitcoind: $(OBJS:obj/%=obj/nogui/%)
$(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) $(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp))
obj/test/%.o: test/%.cpp obj/test/%.o: test/%.cpp
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
@cp $(@:%.o=%.d) $(@:%.o=%.P); \ @cp $(@:%.o=%.d) $(@:%.o=%.P); \
@ -148,7 +150,7 @@ obj/test/%.o: test/%.cpp
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
rm -f $(@:%.o=%.d) rm -f $(@:%.o=%.d)
test_bitcoin: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) test_bitcoin: $(TESTOBJS) $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%))
$(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-Bstatic -lboost_unit_test_framework $(LDFLAGS) $(LIBS) $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-Bstatic -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
clean: clean:

577
src/script.cpp

@ -70,20 +70,186 @@ static inline void popstack(vector<valtype>& stack)
} }
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) 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";
}
return NULL;
}
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";
// meta
case OP_EVAL : return "OP_EVAL";
// expanson
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";
// template matching params
case OP_SCRIPTHASH : return "OP_SCRIPTHASH";
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
return "OP_UNKNOWN";
}
}
//
// Returns true if script is valid.
//
bool EvalScriptInner(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount,
bool fStrictOpEval, int nRecurseDepth)
{ {
CAutoBN_CTX pctx; CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin(); CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end(); CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode; opcodetype opcode;
valtype vchPushValue; valtype vchPushValue;
vector<bool> vfExec; vector<bool> vfExec;
vector<valtype> altstack; vector<valtype> altstack;
if (script.size() > 10000) if (script.size() > 10000)
return false; return false;
int nOpCount = 0;
// Limit OP_EVAL recursion
if (nRecurseDepth > 2)
return false;
try try
{ {
@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Control // Control
// //
case OP_NOP: case OP_NOP:
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
break; break;
@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend); CScript scriptCode(pbegincodehash, pendcodehash);
// Drop the signature, since there's no way for a signature to sign itself // Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig)); scriptCode.FindAndDelete(CScript(vchSig));
bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
nSigOpCount++;
popstack(stack); popstack(stack);
popstack(stack); popstack(stack);
@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return false; return false;
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend); CScript scriptCode(pbegincodehash, pendcodehash);
// Drop the signatures, since there's no way for a signature to sign itself // Drop the signatures, since there's no way for a signature to sign itself
for (int k = 0; k < nSigsCount; k++) for (int k = 0; k < nSigsCount; k++)
@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
} }
ikey++; ikey++;
nKeysCount--; nKeysCount--;
nSigOpCount++;
// If there are more signatures left than keys left, // If there are more signatures left than keys left,
// then too many signatures have failed // then too many signatures have failed
@ -844,6 +1012,32 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
} }
break; break;
case OP_EVAL:
{
if (!fStrictOpEval)
break; // Act as a NO_OP
// Evaluate the top item on the stack as a Script
// [serialized script ] -- [result(s) of executing script]
if (stack.size() < 1)
return false;
valtype& vchScript = stacktop(-1);
CScript subscript(vchScript.begin(), vchScript.end());
popstack(stack);
// Codeseparators not allowed; they don't make sense 'inside' an OP_EVAL, because
// their purpose is to change which parts of the scriptPubKey script is copied
// and signed by OP_CHECKSIG, but OP_EVAl'ed code is in the scriptSig, not the scriptPubKey.
if (subscript.Find(OP_CODESEPARATOR))
return false;
if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType,
pbegincodehash, pendcodehash, nOpCount, nSigOpCount, fStrictOpEval, nRecurseDepth++))
return false;
}
break;
default: default:
return false; return false;
} }
@ -865,6 +1059,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return true; return true;
} }
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script,
const CTransaction& txTo, unsigned int nIn, int nHashType,
bool fStrictOpEval, int& nSigOpCountRet)
{
CScript::const_iterator pbegincodehash = script.begin();
CScript::const_iterator pendcodehash = script.end();
int nOpCount = 0;
return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash,
nOpCount, nSigOpCountRet, fStrictOpEval, 0);
}
@ -963,25 +1169,36 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
//
bool Solver(const CScript& scriptPubKey, vector<pair<opcodetype, valtype> >& vSolutionRet) // Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{ {
// Templates // Templates
static vector<CScript> vTemplates; static map<txnouttype, CScript> mTemplates;
if (vTemplates.empty()) if (mTemplates.empty())
{ {
// Standard tx, sender provides pubkey, receiver adds signature // Standard tx, sender provides pubkey, receiver adds signature
vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); 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));
// Sender provides script hash, receiver provides script and
// as many signatures as required to satisfy script
mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL));
} }
// Scan templates // Scan templates
const CScript& script1 = scriptPubKey; const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const CScript& script2, vTemplates) BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates)
{ {
vSolutionRet.clear(); const CScript& script2 = tplate.second;
vSolutionsRet.clear();
opcodetype opcode1, opcode2; opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2; vector<unsigned char> vch1, vch2;
@ -993,170 +1210,318 @@ bool Solver(const CScript& scriptPubKey, vector<pair<opcodetype, valtype> >& vSo
if (pc1 == script1.end() && pc2 == script2.end()) if (pc1 == script1.end() && pc2 == script2.end())
{ {
// Found a match // Found a match
reverse(vSolutionRet.begin(), vSolutionRet.end()); 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; return true;
} }
if (!script1.GetOp(pc1, opcode1, vch1)) if (!script1.GetOp(pc1, opcode1, vch1))
break; break;
if (!script2.GetOp(pc2, opcode2, vch2)) if (!script2.GetOp(pc2, opcode2, vch2))
break; break;
// Template matching opcodes:
if (opcode2 == OP_PUBKEYS)
{
while (vch1.size() >= 33 && vch1.size() <= 120)
{
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 statments
}
if (opcode2 == OP_PUBKEY) if (opcode2 == OP_PUBKEY)
{ {
if (vch1.size() < 33 || vch1.size() > 120) if (vch1.size() < 33 || vch1.size() > 120)
break; break;
vSolutionRet.push_back(make_pair(opcode2, vch1)); vSolutionsRet.push_back(vch1);
} }
else if (opcode2 == OP_PUBKEYHASH) else if (opcode2 == OP_PUBKEYHASH)
{ {
if (vch1.size() != sizeof(uint160)) if (vch1.size() != sizeof(uint160))
break; break;
vSolutionRet.push_back(make_pair(opcode2, vch1)); vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_SCRIPTHASH)
{
if (vch1.size() != sizeof(uint160))
break;
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_SMALLINTEGER)
{ // Single-byte small integer pushed onto vSolutions
if (opcode1 == OP_0 ||
(opcode1 >= OP_1 && opcode1 <= OP_16))
{
char n = (char)CScript::DecodeOP_N(opcode1);
vSolutionsRet.push_back(valtype(1, n));
}
else
break;
} }
else if (opcode1 != opcode2 || vch1 != vch2) else if (opcode1 != opcode2 || vch1 != vch2)
{ {
// Others must match exactly
break; break;
} }
} }
} }
vSolutionRet.clear(); vSolutionsRet.clear();
typeRet = TX_NONSTANDARD;
return false; return false;
} }
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
{ {
scriptSigRet.clear();
vector<pair<opcodetype, valtype> > vSolution;
if (!Solver(scriptPubKey, vSolution))
return false;
// Compile solution
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
{
if (item.first == OP_PUBKEY)
{
// Sign
const valtype& vchPubKey = item.second;
CKey key; CKey key;
if (!keystore.GetKey(Hash160(vchPubKey), key)) if (!keystore.GetKey(address, key))
return false;
if (key.GetPubKey() != vchPubKey)
return false; return false;
if (hash != 0)
{
vector<unsigned char> vchSig; vector<unsigned char> vchSig;
if (!key.Sign(hash, vchSig)) if (!key.Sign(hash, vchSig))
return false; return false;
vchSig.push_back((unsigned char)nHashType); vchSig.push_back((unsigned char)nHashType);
scriptSigRet << vchSig; 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 (vector<valtype>::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++)
{
const valtype& pubkey = *it;
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
{
++nSigned;
if (nSigned == nRequired) break;
}
}
return nSigned==nRequired;
} }
else if (item.first == OP_PUBKEYHASH)
//
// 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).
// Returns true if scriptPubKey could be completely satisified.
//
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
{ {
// Sign and give pubkey scriptSigRet.clear();
CKey key;
if (!keystore.GetKey(uint160(item.second), key)) txnouttype whichType;
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
if (hash != 0)
CBitcoinAddress address;
CScript subscript;
switch (whichType)
{ {
vector<unsigned char> vchSig; case TX_NONSTANDARD:
if (!key.Sign(hash, vchSig)) return false;
case TX_PUBKEY:
address.SetPubKey(vSolutions[0]);
return Sign1(address, keystore, hash, nHashType, scriptSigRet);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
if (!Sign1(address, keystore, hash, nHashType, scriptSigRet))
return false; return false;
vchSig.push_back((unsigned char)nHashType);
scriptSigRet << vchSig << key.GetPubKey();
}
}
else else
{ {
return false; valtype vch;
keystore.GetPubKey(address, vch);
scriptSigRet << vch;
} }
break;
case TX_SCRIPTHASH:
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
return false;
if (!Solver(keystore, subscript, hash, nHashType, scriptSigRet))
return false;
if (hash != 0)
// static_cast to get vector.operator<< instead of CScript.operator<<
scriptSigRet << static_cast<valtype>(subscript); // signatures AND serialized script
break;
case TX_MULTISIG:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet));
} }
return true; return true;
} }
bool IsStandard(const CScript& scriptPubKey) bool IsStandard(const CScript& scriptPubKey)
{ {
vector<pair<opcodetype, valtype> > vSolution; vector<valtype> vSolutions;
return Solver(scriptPubKey, vSolution); txnouttype whichType;
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;
}
int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
{
int nResult = 0;
BOOST_FOREACH(const valtype& pubkey, pubkeys)
{
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (keystore.HaveKey(address))
++nResult;
}
return nResult;
}
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{ {
vector<pair<opcodetype, valtype> > vSolution; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, vSolution)) txnouttype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
// Compile solution CBitcoinAddress address;
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) switch (whichType)
{ {
if (item.first == OP_PUBKEY) case TX_NONSTANDARD:
{
const valtype& vchPubKey = item.second;
vector<unsigned char> vchPubKeyFound;
if (!keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound))
return false;
if (vchPubKeyFound != vchPubKey)
return false; return false;
} case TX_PUBKEY:
else if (item.first == OP_PUBKEYHASH) address.SetPubKey(vSolutions[0]);
return keystore.HaveKey(address);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
return keystore.HaveKey(address);
case TX_SCRIPTHASH:
{ {
if (!keystore.HaveKey(uint160(item.second))) CScript subscript;
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
return false; return false;
return IsMine(keystore, subscript);
} }
else case TX_MULTISIG:
{ {
return false; // 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);
return HaveKeys(vSolutions, keystore);
} }
} }
return false;
return true;
} }
bool static ExtractAddressInner(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
{ {
vector<pair<opcodetype, valtype> > vSolution; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, vSolution)) txnouttype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) if (whichType == TX_PUBKEY)
{ {
if (item.first == OP_PUBKEY) addressRet.SetPubKey(vSolutions[0]);
addressRet.SetPubKey(item.second);
else if (item.first == OP_PUBKEYHASH)
addressRet.SetHash160((uint160)item.second);
if (keystore == NULL || keystore->HaveKey(addressRet))
return true; return true;
} }
else if (whichType == TX_PUBKEYHASH)
{
addressRet.SetHash160(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
addressRet.SetScriptHash160(uint160(vSolutions[0]));
return true;
}
// Multisig txns have more than one address...
return false; return false;
} }
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txnouttype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
{
addressRet.clear();
typeRet = TX_NONSTANDARD;
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions))
return false;
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) if (typeRet == TX_MULTISIG)
{
nRequiredRet = vSolutions.front()[0];
int n = vSolutions.back()[0];
for (int i = 1; i < vSolutions.size()-1; i++)
{ {
if (keystore) CBitcoinAddress address;
return ExtractAddressInner(scriptPubKey, keystore, addressRet); address.SetPubKey(vSolutions[i]);
addressRet.push_back(address);
}
}
else else
return ExtractAddressInner(scriptPubKey, NULL, addressRet); {
return false; nRequiredRet = 1;
CBitcoinAddress address;
if (typeRet == TX_PUBKEYHASH)
address.SetHash160(uint160(vSolutions.front()));
else if (typeRet == TX_SCRIPTHASH)
address.SetScriptHash160(uint160(vSolutions.front()));
else if (typeRet == TX_PUBKEY)
address.SetPubKey(vSolutions.front());
addressRet.push_back(address);
} }
return true;
}
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType) bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet,
int nHashType, bool fStrictOpEval)
{ {
vector<vector<unsigned char> > stack; vector<vector<unsigned char> > stack;
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet))
return false; return false;
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet))
return false; return false;
if (stack.empty()) if (stack.empty())
return false; return false;
return CastToBool(stack.back()); bool fResult = CastToBool(stack.back());
// This code should be removed when a compatibility-breaking block chain split has passed.
// Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains
// OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op:
if (fResult && fStrictOpEval && (scriptPubKey.Find(OP_EVAL) || scriptSig.Find(OP_EVAL)))
return VerifyScript(scriptSig, scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, false);
return fResult;
} }
@ -1177,15 +1542,16 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
txin.scriptSig = scriptPrereq + txin.scriptSig; txin.scriptSig = scriptPrereq + txin.scriptSig;
// Test solution // Test solution
int nUnused = 0;
if (scriptPrereq.empty()) if (scriptPrereq.empty())
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0, true))
return false; return false;
return true; return true;
} }
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType, bool fStrictOpEval)
{ {
assert(nIn < txTo.vin.size()); assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn]; const CTxIn& txin = txTo.vin[nIn];
@ -1196,8 +1562,35 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
if (txin.prevout.hash != txFrom.GetHash()) if (txin.prevout.hash != txFrom.GetHash())
return false; return false;
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, fStrictOpEval))
return false; return false;
return true; return true;
} }
void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
{
this->clear();
if (address.IsScript())
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL;
else
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
}
void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
{
this->clear();
*this << EncodeOP_N(nRequired);
BOOST_FOREACH(const CKey& key, keys)
*this << key.GetPubKey();
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
}
void CScript::SetEval(const CScript& subscript)
{
assert(!subscript.empty());
uint160 subscriptHash = Hash160(subscript);
this->clear();
*this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL;
}

243
src/script.h

@ -6,7 +6,6 @@
#define H_BITCOIN_SCRIPT #define H_BITCOIN_SCRIPT
#include "base58.h" #include "base58.h"
#include "keystore.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -14,6 +13,7 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
class CTransaction; class CTransaction;
class CKeyStore;
enum enum
{ {
@ -24,6 +24,17 @@ enum
}; };
enum txnouttype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
};
const char* GetTxnOutputType(txnouttype t);
enum opcodetype enum opcodetype
{ {
@ -147,8 +158,10 @@ enum opcodetype
OP_CHECKMULTISIG, OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY, OP_CHECKMULTISIGVERIFY,
// meta
OP_EVAL, // Was OP_NOP1
// expansion // expansion
OP_NOP1,
OP_NOP2, OP_NOP2,
OP_NOP3, OP_NOP3,
OP_NOP4, OP_NOP4,
@ -162,162 +175,16 @@ enum opcodetype
// template matching params // template matching params
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_SCRIPTHASH = 0xfc,
OP_PUBKEYHASH = 0xfd, OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe, OP_PUBKEY = 0xfe,
OP_INVALIDOPCODE = 0xff, OP_INVALIDOPCODE = 0xff,
}; };
const char* GetOpName(opcodetype opcode);
inline 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";
// template matching params
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
return "OP_UNKNOWN";
}
};
@ -574,22 +441,52 @@ public:
return true; return true;
} }
// Encode/decode small integers:
static int DecodeOP_N(opcodetype opcode)
{
if (opcode == OP_0)
return 0;
assert(opcode >= OP_1 && opcode <= OP_16);
return (int)opcode - (int)(OP_1 - 1);
}
static opcodetype EncodeOP_N(int n)
{
assert(n >= 0 && n <= 16);
if (n == 0)
return OP_0;
return (opcodetype)(OP_1+n-1);
}
void FindAndDelete(const CScript& b) int FindAndDelete(const CScript& b)
{ {
int nFound = 0;
if (b.empty()) if (b.empty())
return; return nFound;
iterator pc = begin(); iterator pc = begin();
opcodetype opcode; opcodetype opcode;
do do
{ {
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
{
erase(pc, pc + b.size()); erase(pc, pc + b.size());
++nFound;
}
} }
while (GetOp(pc, opcode)); while (GetOp(pc, opcode));
return nFound;
}
int Find(opcodetype op) const
{
int nFound = 0;
opcodetype opcode;
for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
if (opcode == op)
++nFound;
return nFound;
} }
// This method should be removed when a compatibility-breaking block chain split has passed.
// Compatibility method for old clients that count sigops differently:
int GetSigOpCount() const int GetSigOpCount() const
{ {
int n = 0; int n = 0;
@ -607,11 +504,9 @@ public:
return n; return n;
} }
// Called by CTransaction::IsStandard
bool IsPushOnly() const bool IsPushOnly() const
{ {
if (size() > 200)
return false;
const_iterator pc = begin(); const_iterator pc = begin();
while (pc < end()) while (pc < end())
{ {
@ -625,31 +520,13 @@ public:
} }
CBitcoinAddress GetBitcoinAddress() const void SetBitcoinAddress(const CBitcoinAddress& address);
{
opcodetype opcode;
std::vector<unsigned char> vch;
CScript::const_iterator pc = begin();
if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0;
if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0;
if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0;
uint160 hash160 = uint160(vch);
if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0;
if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0;
if (pc != end()) return 0;
return CBitcoinAddress(hash160);
}
void SetBitcoinAddress(const CBitcoinAddress& address)
{
this->clear();
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
}
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey) void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
{ {
SetBitcoinAddress(CBitcoinAddress(vchPubKey)); SetBitcoinAddress(CBitcoinAddress(vchPubKey));
} }
void SetMultisig(int nRequired, const std::vector<CKey>& keys);
void SetEval(const CScript& subscript);
void PrintHex() const void PrintHex() const
@ -690,14 +567,14 @@ public:
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, bool fStrictOpEval, int& nSigOpCountRet);
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
bool IsStandard(const CScript& scriptPubKey); bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet); bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txnouttype& typeRet, std::vector<CBitcoinAddress>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0, bool fStrictOpEval=true);
#endif #endif

7
src/test/DoS_tests.cpp

@ -5,9 +5,10 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "../main.h" #include "main.h"
#include "../net.h" #include "wallet.h"
#include "../util.h" #include "net.h"
#include "util.h"
using namespace std; using namespace std;

4
src/test/base58_tests.cpp

@ -1,6 +1,8 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../util.h" #include "main.h"
#include "wallet.h"
#include "util.h"
BOOST_AUTO_TEST_SUITE(base58_tests) BOOST_AUTO_TEST_SUITE(base58_tests)

12
src/test/base64_tests.cpp

@ -1,18 +1,20 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../util.h" #include "main.h"
#include "wallet.h"
#include "util.h"
BOOST_AUTO_TEST_SUITE(base64_tests) BOOST_AUTO_TEST_SUITE(base64_tests)
BOOST_AUTO_TEST_CASE(base64_testvectors) BOOST_AUTO_TEST_CASE(base64_testvectors)
{ {
static const string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
static const string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"}; static const std::string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"};
for (int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++) for (int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
{ {
string strEnc = EncodeBase64(vstrIn[i]); std::string strEnc = EncodeBase64(vstrIn[i]);
BOOST_CHECK(strEnc == vstrOut[i]); BOOST_CHECK(strEnc == vstrOut[i]);
string strDec = DecodeBase64(strEnc); std::string strDec = DecodeBase64(strEnc);
BOOST_CHECK(strDec == vstrIn[i]); BOOST_CHECK(strDec == vstrIn[i]);
} }
} }

2
src/test/miner_tests.cpp

@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../uint256.h" #include "uint256.h"
extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); extern void SHA256Transform(void* pstate, void* pinput, const void* pinit);

294
src/test/multisig_tests.cpp

@ -0,0 +1,294 @@
#include <boost/assert.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <openssl/ec.h>
#include <openssl/err.h>
#include "keystore.h"
#include "main.h"
#include "script.h"
#include "wallet.h"
using namespace std;
using namespace boost::assign;
typedef vector<unsigned char> valtype;
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount,
int nHashType, bool fStrictOpEval);
BOOST_AUTO_TEST_SUITE(multisig_tests)
CScript
sign_multisig(CScript scriptPubKey, vector<CKey> keys, CTransaction transaction, int whichIn)
{
uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL);
CScript result;
result << OP_0; // CHECKMULTISIG bug workaround
BOOST_FOREACH(CKey key, keys)
{
vector<unsigned char> vchSig;
BOOST_CHECK(key.Sign(hash, vchSig));
vchSig.push_back((unsigned char)SIGHASH_ALL);
result << vchSig;
}
return result;
}
BOOST_AUTO_TEST_CASE(multisig_verify)
{
CKey key[4];
for (int i = 0; i < 4; i++)
key[i].MakeNewKey();
CScript a_and_b;
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
CScript a_or_b;
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
CTransaction txFrom; // Funding transaction
txFrom.vout.resize(3);
txFrom.vout[0].scriptPubKey = a_and_b;
txFrom.vout[1].scriptPubKey = a_or_b;
txFrom.vout[2].scriptPubKey = escrow;
CTransaction txTo[3]; // Spending transaction
for (int i = 0; i < 3; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
}
vector<CKey> keys;
CScript s;
int nUnused = 0;
// Test a AND b:
keys.clear();
keys += key[0],key[1]; // magic operator+= from boost.assign
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true));
for (int i = 0; i < 4; i++)
{
keys.clear();
keys += key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 1: %d", i));
keys.clear();
keys += key[1],key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 2: %d", i));
}
// Test a OR b:
for (int i = 0; i < 4; i++)
{
keys.clear();
keys += key[i];
s = sign_multisig(a_or_b, keys, txTo[1], 0);
if (i == 0 || i == 1)
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i));
}
s.clear();
s << OP_0 << OP_0;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true));
s.clear();
s << OP_0 << OP_1;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true));
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
{
keys.clear();
keys += key[i],key[j];
s = sign_multisig(escrow, keys, txTo[2], 0);
if (i < j && i < 3 && j < 3)
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 1: %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 2: %d %d", i, j));
}
}
BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{
CKey key[4];
for (int i = 0; i < 4; i++)
key[i].MakeNewKey();
CScript a_and_b;
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(a_and_b));
CScript a_or_b;
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(a_or_b));
CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(escrow));
CScript one_of_four;
one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG;
BOOST_CHECK(!::IsStandard(one_of_four));
CScript malformed[6];
malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG;
malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG;
malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey();
for (int i = 0; i < 6; i++)
BOOST_CHECK(!::IsStandard(malformed[i]));
}
BOOST_AUTO_TEST_CASE(multisig_Solver1)
{
// Tests Solver() that returns lists of keys that are
// required to satisfy a ScriptPubKey
//
// Also tests IsMine() and ExtractAddress()
//
// Note: ExtractAddress for the multisignature transactions
// always returns false for this release, even if you have
// one key that would satisfy an (a|b) or 2-of-3 keys needed
// to spend an escrow transaction.
//
CBasicKeyStore keystore, emptykeystore;
CKey key[3];
CBitcoinAddress keyaddr[3];
for (int i = 0; i < 3; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
keyaddr[i].SetPubKey(key[i].GetPubKey());
}
{
vector<valtype> solutions;
txnouttype whichType;
CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG;
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<valtype> solutions;
txnouttype whichType;
CScript s;
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<valtype> solutions;
txnouttype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4);
CBitcoinAddress addr;
BOOST_CHECK(!ExtractAddress(s, &keystore, addr));
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<valtype> solutions;
txnouttype whichType;
CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4);
vector<CBitcoinAddress> addrs;
int nRequired;
BOOST_CHECK(ExtractAddresses(s, &keystore, whichType, addrs, nRequired));
BOOST_CHECK(addrs[0] == keyaddr[0]);
BOOST_CHECK(addrs[1] == keyaddr[1]);
BOOST_CHECK(nRequired = 1);
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<valtype> solutions;
txnouttype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 5);
}
}
BOOST_AUTO_TEST_CASE(multisig_Sign)
{
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
}
CScript a_and_b;
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
CScript a_or_b;
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
CTransaction txFrom; // Funding transaction
txFrom.vout.resize(3);
txFrom.vout[0].scriptPubKey = a_and_b;
txFrom.vout[1].scriptPubKey = a_or_b;
txFrom.vout[2].scriptPubKey = escrow;
CTransaction txTo[3]; // Spending transaction
for (int i = 0; i < 3; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
}
for (int i = 0; i < 3; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
}
}
BOOST_AUTO_TEST_SUITE_END()

234
src/test/script_op_eval_tests.cpp

@ -0,0 +1,234 @@
#include <boost/assert.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/list_inserter.hpp>
#include <boost/assign/std/vector.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include "../main.h"
#include "../script.h"
#include "../wallet.h"
using namespace std;
// Test routines internal to script.cpp:
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps,
int nHashType, bool fStrictOpEval);
BOOST_AUTO_TEST_SUITE(script_op_eval_tests)
BOOST_AUTO_TEST_CASE(script_op_eval1)
{
// OP_EVAL looks like this:
// scriptSig: <sig> <sig...> <serialized_script>
// scriptPubKey: DUP HASH160 <hash> EQUALVERIFY EVAL
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
CBasicKeyStore keystore;
CKey key[4];
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
}
// 8 Scripts: checking all combinations of
// different keys, straight/EVAL, pubkey/pubkeyhash
CScript standardScripts[4];
standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG;
standardScripts[1].SetBitcoinAddress(key[1].GetPubKey());
standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG;
standardScripts[3].SetBitcoinAddress(key[2].GetPubKey());
CScript evalScripts[4];
uint160 sigScriptHashes[4];
for (int i = 0; i < 4; i++)
{
sigScriptHashes[i] = Hash160(standardScripts[i]);
keystore.AddCScript(sigScriptHashes[i], standardScripts[i]);
evalScripts[i] << OP_DUP << OP_HASH160 << sigScriptHashes[i] << OP_EQUALVERIFY << OP_EVAL;
}
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(8);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = evalScripts[i];
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
}
BOOST_CHECK(txFrom.IsStandard());
CTransaction txTo[8]; // Spending transactions
for (int i = 0; i < 8; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 8; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
}
// All of the above should be OK, and the txTos have valid signatures
// Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
int nUnused = 0;
bool sigOK = VerifySignature(txFrom, txTo[i], 0, nUnused);
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
txTo[i].vin[0].scriptSig = sigSave;
}
}
BOOST_AUTO_TEST_CASE(script_op_eval2)
{
// Test OP_EVAL edge cases
CScript recurse;
recurse << OP_DUP << OP_EVAL;
uint160 recurseHash = Hash160(recurse);
CScript fund;
fund << OP_DUP << OP_HASH160 << recurseHash << OP_EQUALVERIFY << OP_EVAL;
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = fund;
BOOST_CHECK(txFrom.IsStandard()); // Looks like a standard transaction until you try to spend it
CTransaction txTo;
txTo.vin.resize(1);
txTo.vout.resize(1);
txTo.vin[0].prevout.n = 0;
txTo.vin[0].prevout.hash = txFrom.GetHash();
txTo.vin[0].scriptSig = CScript() << static_cast<std::vector<unsigned char> >(recurse);
txTo.vout[0].nValue = 1;
int nUnused = 0;
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true));
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true));
}
BOOST_AUTO_TEST_CASE(script_op_eval3)
{
// Test the CScript::Set* methods
CBasicKeyStore keystore;
CKey key[4];
std::vector<CKey> keys;
for (int i = 0; i < 4; i++)
{
key[i].MakeNewKey();
keystore.AddKey(key[i]);
keys.push_back(key[i]);
}
CScript inner[4];
inner[0].SetBitcoinAddress(key[0].GetPubKey());
inner[1].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+2));
inner[2].SetMultisig(1, std::vector<CKey>(keys.begin(), keys.begin()+2));
inner[3].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+3));
CScript outer[4];
for (int i = 0; i < 4; i++)
{
outer[i].SetEval(inner[i]);
keystore.AddCScript(Hash160(inner[i]), inner[i]);
}
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(4);
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
}
BOOST_CHECK(txFrom.IsStandard());
CTransaction txTo[4]; // Spending transactions
for (int i = 0; i < 4; i++)
{
txTo[i].vin.resize(1);
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
txTo[i].vout[0].nValue = 1;
txTo[i].vout[0].scriptPubKey = inner[i];
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
for (int i = 0; i < 4; i++)
{
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i));
}
}
BOOST_AUTO_TEST_CASE(script_op_eval_backcompat1)
{
// Check backwards-incompatibility-testing code
CScript returnsEleven;
returnsEleven << OP_11;
// This should validate on new clients, but will
// be invalid on old clients (that interpret OP_EVAL as a no-op)
// ... except there's a special rule that makes new clients reject
// it.
CScript fund;
fund << OP_EVAL << OP_11 << OP_EQUAL;
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = fund;
CTransaction txTo;
txTo.vin.resize(1);
txTo.vout.resize(1);
txTo.vin[0].prevout.n = 0;
txTo.vin[0].prevout.hash = txFrom.GetHash();
txTo.vin[0].scriptSig = CScript() << static_cast<std::vector<unsigned char> >(returnsEleven);
txTo.vout[0].nValue = 1;
int nUnused = 0;
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true));
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true));
}
BOOST_AUTO_TEST_CASE(script_op_eval_switchover)
{
// Test OP_EVAL switchover code
CScript notValid;
notValid << OP_11 << OP_12 << OP_EQUALVERIFY;
// This will be valid under old rules, invalid under new:
CScript fund;
fund << OP_EVAL;
CTransaction txFrom; // Funding transaction:
txFrom.vout.resize(1);
txFrom.vout[0].scriptPubKey = fund;
CTransaction txTo;
txTo.vin.resize(1);
txTo.vout.resize(1);
txTo.vin[0].prevout.n = 0;
txTo.vin[0].prevout.hash = txFrom.GetHash();
txTo.vin[0].scriptSig = CScript() << static_cast<std::vector<unsigned char> >(notValid);
txTo.vout[0].nValue = 1;
int nUnused = 0;
BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, false));
// Under strict op_eval switchover, it should be considered invalid:
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true));
}
BOOST_AUTO_TEST_SUITE_END()

46
src/test/script_tests.cpp

@ -2,13 +2,13 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "../main.h" #include "main.h"
#include "../wallet.h" #include "wallet.h"
using namespace std; using namespace std;
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps,
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType); int nHashType, bool fStrictOpEval);
BOOST_AUTO_TEST_SUITE(script_tests) BOOST_AUTO_TEST_SUITE(script_tests)
@ -21,19 +21,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a }; static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a };
static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };
int nUnused = 0;
vector<vector<unsigned char> > directStack; vector<vector<unsigned char> > directStack;
BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0)); BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, true, nUnused));
vector<vector<unsigned char> > pushdata1Stack; vector<vector<unsigned char> > pushdata1Stack;
BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0)); BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, true, nUnused));
BOOST_CHECK(pushdata1Stack == directStack); BOOST_CHECK(pushdata1Stack == directStack);
vector<vector<unsigned char> > pushdata2Stack; vector<vector<unsigned char> > pushdata2Stack;
BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0)); BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, true, nUnused));
BOOST_CHECK(pushdata2Stack == directStack); BOOST_CHECK(pushdata2Stack == directStack);
vector<vector<unsigned char> > pushdata4Stack; vector<vector<unsigned char> > pushdata4Stack;
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0)); BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, true, nUnused));
BOOST_CHECK(pushdata4Stack == directStack); BOOST_CHECK(pushdata4Stack == directStack);
} }
@ -71,6 +73,7 @@ sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
{ {
int nUnused = 0;
CKey key1, key2, key3; CKey key1, key2, key3;
key1.MakeNewKey(); key1.MakeNewKey();
key2.MakeNewKey(); key2.MakeNewKey();
@ -91,19 +94,20 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
txTo12.vout[0].nValue = 1; txTo12.vout[0].nValue = 1;
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0)); BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true));
txTo12.vout[0].nValue = 2; txTo12.vout[0].nValue = 2;
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0)); BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true));
CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, 0)); BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0, true));
CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0)); BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true));
} }
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
{ {
int nUnused = 0;
CKey key1, key2, key3, key4; CKey key1, key2, key3, key4;
key1.MakeNewKey(); key1.MakeNewKey();
key2.MakeNewKey(); key2.MakeNewKey();
@ -127,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
std::vector<CKey> keys; std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2); keys.push_back(key1); keys.push_back(key2);
CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key1); keys.push_back(key3); keys.push_back(key1); keys.push_back(key3);
CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key2); keys.push_back(key3); keys.push_back(key2); keys.push_back(key3);
CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key2); keys.push_back(key2); // Can't re-use sig keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); keys.clear();
keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0, true));
keys.clear(); // Must have signatures keys.clear(); // Must have signatures
CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, 0)); BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0, true));
} }

23
src/test/test_bitcoin.cpp

@ -1,19 +1,18 @@
#define BOOST_TEST_MODULE Bitcoin Test Suite #define BOOST_TEST_MODULE Bitcoin Test Suite
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../main.h" #include "main.h"
#include "../wallet.h" #include "wallet.h"
#include "uint160_tests.cpp" extern bool fPrintToConsole;
#include "uint256_tests.cpp" struct TestingSetup {
#include "script_tests.cpp" TestingSetup() {
#include "transaction_tests.cpp" fPrintToConsole = true; // don't want to write to debug.log file
#include "DoS_tests.cpp" }
#include "base64_tests.cpp" ~TestingSetup() { }
#include "util_tests.cpp" };
#include "base58_tests.cpp"
#include "miner_tests.cpp" BOOST_GLOBAL_FIXTURE(TestingSetup);
#include "Checkpoints_tests.cpp"
CWallet* pwalletMain; CWallet* pwalletMain;

4
src/test/transaction_tests.cpp

@ -1,7 +1,7 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../main.h" #include "main.h"
#include "../wallet.h" #include "wallet.h"
using namespace std; using namespace std;

2
src/test/uint160_tests.cpp

@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../uint256.h" #include "uint256.h"
BOOST_AUTO_TEST_SUITE(uint160_tests) BOOST_AUTO_TEST_SUITE(uint160_tests)

2
src/test/uint256_tests.cpp

@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include "../uint256.h" #include "uint256.h"
BOOST_AUTO_TEST_SUITE(uint256_tests) BOOST_AUTO_TEST_SUITE(uint256_tests)

4
src/test/util_tests.cpp

@ -2,7 +2,9 @@
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "../util.h" #include "main.h"
#include "wallet.h"
#include "util.h"
using namespace std; using namespace std;

37
src/wallet.cpp

@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
return false; return false;
} }
bool CWallet::AddCScript(const uint160 &hash, const CScript& redeemScript)
{
if (!CCryptoKeyStore::AddCScript(hash, redeemScript))
return false;
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteCScript(hash, redeemScript);
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase) bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{ {
if (!IsLocked()) if (!IsLocked())
@ -374,6 +383,24 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
return 0; return 0;
} }
bool CWallet::IsChange(const CTxOut& txout) const
{
CBitcoinAddress address;
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
// is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address;
// a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change).
if (ExtractAddress(txout.scriptPubKey, this, address))
CRITICAL_BLOCK(cs_wallet)
if (!mapAddressBook.count(address))
return true;
return false;
}
int64 CWalletTx::GetTxTime() const int64 CWalletTx::GetTxTime() const
{ {
return nTimeReceived; return nTimeReceived;
@ -443,8 +470,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
// Sent/received. Standard client will never generate a send-to-multiple-recipients, // Sent/received.
// but non-standard clients might (so return a list of address/amount pairs)
BOOST_FOREACH(const CTxOut& txout, vout) BOOST_FOREACH(const CTxOut& txout, vout)
{ {
CBitcoinAddress address; CBitcoinAddress address;
@ -997,12 +1023,11 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
vector<unsigned char> vchPubKey = reservekey.GetReservedKey(); vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey)); // assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself, using same address type as the payment // Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
CScript scriptChange; CScript scriptChange;
if (vecSend[0].first.GetBitcoinAddress().IsValid())
scriptChange.SetBitcoinAddress(vchPubKey); scriptChange.SetBitcoinAddress(vchPubKey);
else
scriptChange << vchPubKey << OP_CHECKSIG;
// Insert change txn at random position: // Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());

13
src/wallet.h

@ -7,6 +7,7 @@
#include "bignum.h" #include "bignum.h"
#include "key.h" #include "key.h"
#include "keystore.h"
#include "script.h" #include "script.h"
class CWalletTx; class CWalletTx;
@ -69,6 +70,8 @@ public:
bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret); bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
// Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
bool AddCScript(const uint160& hash, const CScript& redeemScript);
bool LoadCScript(const uint160& hash, const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(hash, redeemScript); }
bool Unlock(const SecureString& strWalletPassphrase); bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
@ -114,15 +117,7 @@ public:
throw std::runtime_error("CWallet::GetCredit() : value out of range"); throw std::runtime_error("CWallet::GetCredit() : value out of range");
return (IsMine(txout) ? txout.nValue : 0); return (IsMine(txout) ? txout.nValue : 0);
} }
bool IsChange(const CTxOut& txout) const bool IsChange(const CTxOut& txout) const;
{
CBitcoinAddress address;
if (ExtractAddress(txout.scriptPubKey, this, address))
CRITICAL_BLOCK(cs_wallet)
if (!mapAddressBook.count(address))
return true;
return false;
}
int64 GetChange(const CTxOut& txout) const int64 GetChange(const CTxOut& txout) const
{ {
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))

Loading…
Cancel
Save