aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2011-12-20 14:43:31 -0500
committerGavin Andresen <gavinandresen@gmail.com>2011-12-20 14:43:31 -0500
commit595925592d36fb5d5d34beea3c3e71fca2b6726e (patch)
treee28c34353aa08a6f3c0368a39982970c08245ea3
parentf06e3e0ea6c8da90585a9f0936c390659dcece37 (diff)
parent77f21f1583deb89bf3fffe80fe9b181fedb1dd60 (diff)
Merge branch 'op_eval'
-rw-r--r--src/base58.h48
-rw-r--r--src/bitcoinrpc.cpp104
-rw-r--r--src/crypter.h1
-rw-r--r--src/db.cpp9
-rw-r--r--src/db.h27
-rw-r--r--src/keystore.cpp33
-rw-r--r--src/keystore.h11
-rw-r--r--src/main.cpp257
-rw-r--r--src/main.h38
-rw-r--r--src/makefile.osx5
-rw-r--r--src/makefile.unix6
-rw-r--r--src/script.cpp607
-rw-r--r--src/script.h243
-rw-r--r--src/test/DoS_tests.cpp7
-rw-r--r--src/test/base58_tests.cpp4
-rw-r--r--src/test/base64_tests.cpp12
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/multisig_tests.cpp294
-rw-r--r--src/test/script_op_eval_tests.cpp234
-rw-r--r--src/test/script_tests.cpp46
-rw-r--r--src/test/test_bitcoin.cpp23
-rw-r--r--src/test/transaction_tests.cpp4
-rw-r--r--src/test/uint160_tests.cpp2
-rw-r--r--src/test/uint256_tests.cpp2
-rw-r--r--src/test/util_tests.cpp4
-rw-r--r--src/wallet.cpp39
-rw-r--r--src/wallet.h13
27 files changed, 1615 insertions, 460 deletions
diff --git a/src/base58.h b/src/base58.h
index 113fa14a5e..cba638f4e0 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -253,14 +253,26 @@ public:
};
// 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
+// 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
{
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)
@@ -268,16 +280,34 @@ public:
SetHash160(Hash160(vchPubKey));
}
+ bool SetScriptHash160(const uint160& hash160)
+ {
+ SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &hash160, 20);
+ return true;
+ }
+
bool IsValid() const
{
int nExpectedSize = 20;
bool fExpectTestNet = false;
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;
- case 111:
+ case PUBKEY_ADDRESS_TEST_LEGACY:
+ case PUBKEY_ADDRESS_TEST:
+ nExpectedSize = 20;
+ fExpectTestNet = true;
+ break;
+ case SCRIPT_ADDRESS_TEST:
+ nExpectedSize = 20;
fExpectTestNet = true;
break;
@@ -286,6 +316,14 @@ public:
}
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()
{
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index fdb1b34a0a..bed90d4f6c 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -673,7 +673,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
if (params.size() > 1)
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]);
set<CBitcoinAddress> setAddress;
GetAccountAddresses(strAccount, setAddress);
@@ -942,6 +942,68 @@ Value sendmany(const Array& params, bool fHelp)
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
{
@@ -1602,7 +1664,35 @@ Value validateaddress(const Array& params, bool fHelp)
// version of the address:
string currentAddress = address.ToString();
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))
ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
}
@@ -1847,6 +1937,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany),
+ make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage),
@@ -2490,6 +2581,15 @@ int CommandLineRPC(int argc, char *argv[])
params[1] = v.get_obj();
}
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
Object reply = CallRPC(strMethod, params);
diff --git a/src/crypter.h b/src/crypter.h
index d7f8a39d83..e52ebc01c7 100644
--- a/src/crypter.h
+++ b/src/crypter.h
@@ -4,6 +4,7 @@
#ifndef __CRYPTER_H__
#define __CRYPTER_H__
+#include "util.h" /* for SecureString */
#include "key.h"
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
diff --git a/src/db.cpp b/src/db.cpp
index 7195e065ea..f43b2a5656 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -934,6 +934,15 @@ int CWalletDB::LoadWallet(CWallet* pwallet)
if (nMinVersion > CLIENT_VERSION)
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();
}
diff --git a/src/db.h b/src/db.h
index 15bfb29c8e..e593ae28ed 100644
--- a/src/db.h
+++ b/src/db.h
@@ -13,17 +13,17 @@
#include <db_cxx.h>
-class CTxIndex;
+class CAccount;
+class CAccountingEntry;
+class CAddress;
+class CBlockLocator;
class CDiskBlockIndex;
class CDiskTxPos;
+class CMasterKey;
class COutPoint;
-class CAddress;
-class CWalletTx;
+class CTxIndex;
class CWallet;
-class CAccount;
-class CAccountingEntry;
-class CBlockLocator;
-
+class CWalletTx;
extern unsigned int nWalletDBUpdated;
extern DbEnv dbenv;
@@ -420,6 +420,19 @@ public:
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)
{
nWalletDBUpdated++;
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 6cf557fafe..21fb0f91b2 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -4,8 +4,9 @@
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h"
-#include "db.h"
#include "crypter.h"
+#include "db.h"
+#include "script.h"
std::vector<unsigned char> CKeyStore::GenerateNewKey()
{
@@ -33,6 +34,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
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()
{
CRITICAL_BLOCK(cs_KeyStore)
diff --git a/src/keystore.h b/src/keystore.h
index 3b91de6fb4..669bf90174 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -6,6 +6,7 @@
#define BITCOIN_KEYSTORE_H
#include "crypter.h"
+#include "script.h"
// A virtual base class for key stores
class CKeyStore
@@ -31,6 +32,11 @@ public:
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
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
virtual std::vector<unsigned char> GenerateNewKey();
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
@@ -44,12 +50,14 @@ public:
};
typedef std::map<CBitcoinAddress, CSecret> KeyMap;
+typedef std::map<uint160, CScript > ScriptMap;
// Basic key store, that keeps keys in an address->secret map
class CBasicKeyStore : public CKeyStore
{
protected:
KeyMap mapKeys;
+ ScriptMap mapScripts;
public:
bool AddKey(const CKey& key);
@@ -86,6 +94,9 @@ public:
}
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;
diff --git a/src/main.cpp b/src/main.cpp
index e2f3cc9f84..cfd6ac2c46 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -246,6 +246,67 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
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)
@@ -369,15 +430,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ((int64)nLockTime > std::numeric_limits<int>::max())
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)
if (!fTestNet && !IsStandard())
return error("AcceptToMemoryPool() : nonstandard transaction type");
@@ -421,15 +473,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if (fCheckInputs)
{
- // Check against previous transactions
+ map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
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;
- 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)
*pfMissingInputs = true;
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
if (nFees < GetMinFee(1000, true, true))
@@ -826,8 +897,61 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
}
-bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
- CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
+bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
+ bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
+{
+ if (IsCoinBase())
+ return true; // Coinbase transactions have no inputs to fetch.
+
+ for (int i = 0; i < vin.size(); i++)
+ {
+ COutPoint prevout = vin[i].prevout;
+ if (inputsRet.count(prevout.hash))
+ continue; // Got it already
+
+ // Read txindex
+ CTxIndex& txindex = inputsRet[prevout.hash].first;
+ bool fFound = true;
+ if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
+ {
+ // Get txindex from current proposed changes
+ txindex = mapTestPool.find(prevout.hash)->second;
+ }
+ else
+ {
+ // Read txindex from txdb
+ fFound = txdb.ReadTxIndex(prevout.hash, txindex);
+ }
+ if (!fFound && (fBlock || fMiner))
+ 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
+ CTransaction& txPrev = inputsRet[prevout.hash].second;
+ if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
+ {
+ // Get prev tx from single transactions in memory
+ CRITICAL_BLOCK(cs_mapTransactions)
+ {
+ if (!mapTransactions.count(prevout.hash))
+ 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];
+ }
+ if (!fFound)
+ txindex.vSpent.resize(txPrev.vout.size());
+ }
+ else
+ {
+ // Get prev tx from disk
+ if (!txPrev.ReadFromDisk(txindex.pos))
+ 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
@@ -839,43 +963,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
-
- // Read txindex
- CTxIndex txindex;
- bool fFound = true;
- if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
- {
- // Get txindex from current proposed changes
- txindex = mapTestPool[prevout.hash];
- }
- else
- {
- // Read txindex from txdb
- fFound = txdb.ReadTxIndex(prevout.hash, txindex);
- }
- 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());
-
- // Read txPrev
- CTransaction txPrev;
- if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
- {
- // Get prev tx from single transactions in memory
- CRITICAL_BLOCK(cs_mapTransactions)
- {
- 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());
- txPrev = mapTransactions[prevout.hash];
- }
- if (!fFound)
- txindex.vSpent.resize(txPrev.vout.size());
- }
- else
- {
- // Get prev tx from disk
- 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());
- }
+ 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())
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
// still computed and checked, and any change will be caught at the next checkpoint.
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
- 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()));
+ }
// Check for conflicts (double-spend)
// 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;
// Verify signature
- if (!VerifySignature(txPrev, *this, i))
+ int nUnused = 0;
+ if (!VerifySignature(txPrev, *this, i, nUnused, false))
return error("ConnectInputs() : VerifySignature failed");
///// 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;
int64 nFees = 0;
+ int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
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;
+ if (nSigOps > MAX_BLOCK_SIGOPS)
+ return DoS(100, error("ConnectBlock() : too many sigops"));
}
+
// Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
@@ -1291,8 +1407,21 @@ bool CBlock::CheckBlock() const
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
- // Check that it's not full of nonstandard transactions
- if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
+ // This code should be removed when a compatibility-breaking block chain split has passed.
+ // 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"));
// Check merkleroot
@@ -2827,9 +2956,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue;
- int nTxSigOps = tx.GetSigOpCount();
- if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
- continue;
// Transaction fee required depends on block size
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
// because we're already processing them in order of dependency
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;
swap(mapTestPool, mapTestPoolTmp);
@@ -2887,6 +3019,13 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int&
}
++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();
}
diff --git a/src/main.h b/src/main.h
index 67524ef4be..4543065d72 100644
--- a/src/main.h
+++ b/src/main.h
@@ -493,26 +493,8 @@ public:
return (vin.size() == 1 && vin[0].prevout.IsNull());
}
- int GetSigOpCount() 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;
- }
+ bool IsStandard() const;
+ bool AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
int64 GetValueOut() const
{
@@ -640,8 +622,13 @@ public:
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
bool ReadFromDisk(COutPoint prevout);
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 CheckTransaction() const;
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
@@ -851,13 +838,6 @@ public:
return (int64)nTime;
}
- int GetSigOpCount() const
- {
- int n = 0;
- BOOST_FOREACH(const CTransaction& tx, vtx)
- n += tx.GetSigOpCount();
- return n;
- }
uint256 BuildMerkleTree() const
diff --git a/src/makefile.osx b/src/makefile.osx
index 4b0b521a33..bd72e6bc03 100644
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -10,6 +10,7 @@ CXX=llvm-g++
DEPSDIR=/opt/local
INCLUDEPATHS= \
+ -I"$(CURDIR)" \
-I"$(DEPSDIR)/include" \
-I"$(DEPSDIR)/include/db48"
@@ -112,6 +113,8 @@ obj/nogui/%.o: %.cpp
bitcoind: $(OBJS:obj/%=obj/nogui/%)
$(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS)
+TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp))
+
obj/test/%.o: test/%.cpp
$(CXX) -c $(CFLAGS) -MMD -o $@ $<
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
@@ -119,7 +122,7 @@ obj/test/%.o: test/%.cpp
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
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
clean:
diff --git a/src/makefile.unix b/src/makefile.unix
index a436f968bc..055f7ab297 100644
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -6,7 +6,7 @@ USE_UPNP:=0
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))
LMODE = dynamic
@@ -141,6 +141,8 @@ obj/nogui/%.o: %.cpp
bitcoind: $(OBJS:obj/%=obj/nogui/%)
$(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp))
+
obj/test/%.o: test/%.cpp
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
@@ -148,7 +150,7 @@ obj/test/%.o: test/%.cpp
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
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)
clean:
diff --git a/src/script.cpp b/src/script.cpp
index 12d3f9e83a..467cb2f0df 100644
--- a/src/script.cpp
+++ b/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;
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
- CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode;
valtype vchPushValue;
vector<bool> vfExec;
vector<valtype> altstack;
if (script.size() > 10000)
return false;
- int nOpCount = 0;
+ // Limit OP_EVAL recursion
+ if (nRecurseDepth > 2)
+ return false;
try
{
@@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Control
//
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:
break;
@@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
// 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
scriptCode.FindAndDelete(CScript(vchSig));
bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+ nSigOpCount++;
popstack(stack);
popstack(stack);
@@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return false;
// 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
for (int k = 0; k < nSigsCount; k++)
@@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}
ikey++;
nKeysCount--;
+ nSigOpCount++;
// If there are more signatures left than keys left,
// then too many signatures have failed
@@ -844,6 +1012,32 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}
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:
return false;
}
@@ -865,6 +1059,18 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
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
- static vector<CScript> vTemplates;
- if (vTemplates.empty())
+ static map<txnouttype, CScript> mTemplates;
+ if (mTemplates.empty())
{
// 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
- 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
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;
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())
{
// 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;
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
break;
+
+ // Template matching opcodes:
+ if (opcode2 == OP_PUBKEYS)
+ {
+ while (vch1.size() >= 33 && vch1.size() <= 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 (vch1.size() < 33 || vch1.size() > 120)
break;
- vSolutionRet.push_back(make_pair(opcode2, vch1));
+ vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_PUBKEYHASH)
{
if (vch1.size() != sizeof(uint160))
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)
{
+ // Others must match exactly
break;
}
}
}
- vSolutionRet.clear();
+ vSolutionsRet.clear();
+ typeRet = TX_NONSTANDARD;
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();
+ CKey key;
+ if (!keystore.GetKey(address, key))
+ return false;
- vector<pair<opcodetype, valtype> > vSolution;
- if (!Solver(scriptPubKey, vSolution))
+ vector<unsigned char> vchSig;
+ if (!key.Sign(hash, vchSig))
return false;
+ vchSig.push_back((unsigned char)nHashType);
+ scriptSigRet << vchSig;
+
+ return true;
+}
- // Compile solution
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+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++)
{
- if (item.first == OP_PUBKEY)
- {
- // Sign
- const valtype& vchPubKey = item.second;
- CKey key;
- if (!keystore.GetKey(Hash160(vchPubKey), key))
- return false;
- if (key.GetPubKey() != vchPubKey)
- return false;
- if (hash != 0)
- {
- vector<unsigned char> vchSig;
- if (!key.Sign(hash, vchSig))
- return false;
- vchSig.push_back((unsigned char)nHashType);
- scriptSigRet << vchSig;
- }
- }
- else if (item.first == OP_PUBKEYHASH)
+ const valtype& pubkey = *it;
+ CBitcoinAddress address;
+ address.SetPubKey(pubkey);
+ if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
{
- // Sign and give pubkey
- CKey key;
- if (!keystore.GetKey(uint160(item.second), key))
- return false;
- if (hash != 0)
- {
- vector<unsigned char> vchSig;
- if (!key.Sign(hash, vchSig))
- return false;
- vchSig.push_back((unsigned char)nHashType);
- scriptSigRet << vchSig << key.GetPubKey();
- }
+ ++nSigned;
+ if (nSigned == nRequired) break;
}
+ }
+ return nSigned==nRequired;
+}
+
+//
+// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
+// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed).
+// Returns true if scriptPubKey could be completely satisified.
+//
+bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
+{
+ scriptSigRet.clear();
+
+ txnouttype whichType;
+ vector<valtype> vSolutions;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
+ return false;
+
+ CBitcoinAddress address;
+ CScript subscript;
+ switch (whichType)
+ {
+ case TX_NONSTANDARD:
+ 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;
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;
}
bool IsStandard(const CScript& scriptPubKey)
{
- vector<pair<opcodetype, valtype> > vSolution;
- return Solver(scriptPubKey, vSolution);
+ vector<valtype> vSolutions;
+ 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)
{
- vector<pair<opcodetype, valtype> > vSolution;
- if (!Solver(scriptPubKey, vSolution))
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
- // Compile solution
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+ CBitcoinAddress address;
+ switch (whichType)
{
- if (item.first == OP_PUBKEY)
- {
- const valtype& vchPubKey = item.second;
- vector<unsigned char> vchPubKeyFound;
- if (!keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound))
- return false;
- if (vchPubKeyFound != vchPubKey)
- return false;
- }
- else if (item.first == OP_PUBKEYHASH)
- {
- if (!keystore.HaveKey(uint160(item.second)))
- return false;
- }
- else
- {
+ case TX_NONSTANDARD:
+ return false;
+ case TX_PUBKEY:
+ address.SetPubKey(vSolutions[0]);
+ return keystore.HaveKey(address);
+ case TX_PUBKEYHASH:
+ address.SetHash160(uint160(vSolutions[0]));
+ return keystore.HaveKey(address);
+ case TX_SCRIPTHASH:
+ {
+ CScript subscript;
+ if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
return false;
- }
+ return IsMine(keystore, subscript);
}
-
- return true;
+ case TX_MULTISIG:
+ {
+ // Only consider transactions "mine" if we own ALL the
+ // keys involved. multi-signature transactions that are
+ // partially owned (somebody else has a key that can spend
+ // them) enable spend-out-from-under-you attacks, especially
+ // in shared-wallet situations.
+ vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
+ return HaveKeys(vSolutions, keystore);
+ }
+ }
+ return false;
}
-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;
- if (!Solver(scriptPubKey, vSolution))
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution)
+ if (whichType == TX_PUBKEY)
{
- if (item.first == OP_PUBKEY)
- addressRet.SetPubKey(item.second);
- else if (item.first == OP_PUBKEYHASH)
- addressRet.SetHash160((uint160)item.second);
- if (keystore == NULL || keystore->HaveKey(addressRet))
- return true;
+ addressRet.SetPubKey(vSolutions[0]);
+ 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;
}
-
-bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
+bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txnouttype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
{
- if (keystore)
- return ExtractAddressInner(scriptPubKey, keystore, addressRet);
+ addressRet.clear();
+ typeRet = TX_NONSTANDARD;
+ vector<valtype> vSolutions;
+ if (!Solver(scriptPubKey, typeRet, vSolutions))
+ return false;
+
+ if (typeRet == TX_MULTISIG)
+ {
+ nRequiredRet = vSolutions.front()[0];
+ int n = vSolutions.back()[0];
+ for (int i = 1; i < vSolutions.size()-1; i++)
+ {
+ CBitcoinAddress address;
+ address.SetPubKey(vSolutions[i]);
+ addressRet.push_back(address);
+ }
+ }
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;
- if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet))
return false;
- if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet))
return false;
if (stack.empty())
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;
// Test solution
+ int nUnused = 0;
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 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());
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())
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 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;
+}
diff --git a/src/script.h b/src/script.h
index e61ea2fd7e..b671e15963 100644
--- a/src/script.h
+++ b/src/script.h
@@ -6,7 +6,6 @@
#define H_BITCOIN_SCRIPT
#include "base58.h"
-#include "keystore.h"
#include <string>
#include <vector>
@@ -14,6 +13,7 @@
#include <boost/foreach.hpp>
class CTransaction;
+class CKeyStore;
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
{
@@ -147,8 +158,10 @@ enum opcodetype
OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY,
+ // meta
+ OP_EVAL, // Was OP_NOP1
+
// expansion
- OP_NOP1,
OP_NOP2,
OP_NOP3,
OP_NOP4,
@@ -162,162 +175,16 @@ enum opcodetype
// template matching params
+ OP_SMALLINTEGER = 0xfa,
+ OP_PUBKEYS = 0xfb,
+ OP_SCRIPTHASH = 0xfc,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,
OP_INVALIDOPCODE = 0xff,
};
-
-
-
-
-
-
-
-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";
- }
-};
-
+const char* GetOpName(opcodetype opcode);
@@ -574,22 +441,52 @@ public:
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())
- return;
+ return nFound;
iterator pc = begin();
opcodetype opcode;
do
{
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
+ {
erase(pc, pc + b.size());
+ ++nFound;
+ }
}
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 n = 0;
@@ -607,11 +504,9 @@ public:
return n;
}
-
+ // Called by CTransaction::IsStandard
bool IsPushOnly() const
{
- if (size() > 200)
- return false;
const_iterator pc = begin();
while (pc < end())
{
@@ -625,31 +520,13 @@ public:
}
- CBitcoinAddress GetBitcoinAddress() const
- {
- 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 CBitcoinAddress& address);
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
{
SetBitcoinAddress(CBitcoinAddress(vchPubKey));
}
+ void SetMultisig(int nRequired, const std::vector<CKey>& keys);
+ void SetEval(const CScript& subscript);
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 EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
-
+bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
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 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
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index 01e6691254..e9b7b4517a 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -5,9 +5,10 @@
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
-#include "../main.h"
-#include "../net.h"
-#include "../util.h"
+#include "main.h"
+#include "wallet.h"
+#include "net.h"
+#include "util.h"
using namespace std;
diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp
index c7fa74e96d..d52ac74982 100644
--- a/src/test/base58_tests.cpp
+++ b/src/test/base58_tests.cpp
@@ -1,6 +1,8 @@
#include <boost/test/unit_test.hpp>
-#include "../util.h"
+#include "main.h"
+#include "wallet.h"
+#include "util.h"
BOOST_AUTO_TEST_SUITE(base58_tests)
diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp
index f30f7f8936..fff30ef5eb 100644
--- a/src/test/base64_tests.cpp
+++ b/src/test/base64_tests.cpp
@@ -1,18 +1,20 @@
#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_CASE(base64_testvectors)
{
- static const string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
- static const string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"};
+ static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"};
+ static const std::string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"};
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]);
- string strDec = DecodeBase64(strEnc);
+ std::string strDec = DecodeBase64(strEnc);
BOOST_CHECK(strDec == vstrIn[i]);
}
}
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 8fc534762a..d4abd6d2a7 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp>
-#include "../uint256.h"
+#include "uint256.h"
extern void SHA256Transform(void* pstate, void* pinput, const void* pinit);
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
new file mode 100644
index 0000000000..58f62b9542
--- /dev/null
+++ b/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()
diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp
new file mode 100644
index 0000000000..c44642c6e9
--- /dev/null
+++ b/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()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 13feb86b97..22885a6438 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -2,13 +2,13 @@
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
-#include "../main.h"
-#include "../wallet.h"
+#include "main.h"
+#include "wallet.h"
using namespace std;
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 VerifySignature(const CTransaction& txFrom, 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_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 pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };
+ int nUnused = 0;
+
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;
- 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);
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);
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);
}
@@ -71,6 +73,7 @@ sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
{
+ int nUnused = 0;
CKey key1, key2, key3;
key1.MakeNewKey();
key2.MakeNewKey();
@@ -91,19 +94,20 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
txTo12.vout[0].nValue = 1;
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;
- 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);
- 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);
- BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0));
+ BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true));
}
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
{
+ int nUnused = 0;
CKey key1, key2, key3, key4;
key1.MakeNewKey();
key2.MakeNewKey();
@@ -127,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
std::vector<CKey> keys;
keys.push_back(key1); keys.push_back(key2);
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.push_back(key1); keys.push_back(key3);
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.push_back(key2); keys.push_back(key3);
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.push_back(key2); keys.push_back(key2); // Can't re-use sig
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.push_back(key2); keys.push_back(key1); // sigs must be in correct order
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.push_back(key3); keys.push_back(key2); // sigs must be in correct order
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.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
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.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
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
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));
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 39a7c88e13..4b52b74cfe 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -1,19 +1,18 @@
#define BOOST_TEST_MODULE Bitcoin Test Suite
#include <boost/test/unit_test.hpp>
-#include "../main.h"
-#include "../wallet.h"
+#include "main.h"
+#include "wallet.h"
-#include "uint160_tests.cpp"
-#include "uint256_tests.cpp"
-#include "script_tests.cpp"
-#include "transaction_tests.cpp"
-#include "DoS_tests.cpp"
-#include "base64_tests.cpp"
-#include "util_tests.cpp"
-#include "base58_tests.cpp"
-#include "miner_tests.cpp"
-#include "Checkpoints_tests.cpp"
+extern bool fPrintToConsole;
+struct TestingSetup {
+ TestingSetup() {
+ fPrintToConsole = true; // don't want to write to debug.log file
+ }
+ ~TestingSetup() { }
+};
+
+BOOST_GLOBAL_FIXTURE(TestingSetup);
CWallet* pwalletMain;
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index e6eb0f054f..3268343bbe 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,7 +1,7 @@
#include <boost/test/unit_test.hpp>
-#include "../main.h"
-#include "../wallet.h"
+#include "main.h"
+#include "wallet.h"
using namespace std;
diff --git a/src/test/uint160_tests.cpp b/src/test/uint160_tests.cpp
index 42c8275afe..35cb35b25a 100644
--- a/src/test/uint160_tests.cpp
+++ b/src/test/uint160_tests.cpp
@@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp>
-#include "../uint256.h"
+#include "uint256.h"
BOOST_AUTO_TEST_SUITE(uint160_tests)
diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp
index c5d45e215e..efdc8a6aeb 100644
--- a/src/test/uint256_tests.cpp
+++ b/src/test/uint256_tests.cpp
@@ -1,6 +1,6 @@
#include <boost/test/unit_test.hpp>
-#include "../uint256.h"
+#include "uint256.h"
BOOST_AUTO_TEST_SUITE(uint256_tests)
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 8c8b99e1b2..9571c47382 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -2,7 +2,9 @@
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
-#include "../util.h"
+#include "main.h"
+#include "wallet.h"
+#include "util.h"
using namespace std;
diff --git a/src/wallet.cpp b/src/wallet.cpp
index c6f5795c0b..539452841f 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
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)
{
if (!IsLocked())
@@ -374,6 +383,24 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
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
{
return nTimeReceived;
@@ -443,8 +470,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
nFee = nDebit - nValueOut;
}
- // Sent/received. Standard client will never generate a send-to-multiple-recipients,
- // but non-standard clients might (so return a list of address/amount pairs)
+ // Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
CBitcoinAddress address;
@@ -997,12 +1023,11 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// 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;
- if (vecSend[0].first.GetBitcoinAddress().IsValid())
- scriptChange.SetBitcoinAddress(vchPubKey);
- else
- scriptChange << vchPubKey << OP_CHECKSIG;
+ scriptChange.SetBitcoinAddress(vchPubKey);
// Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
diff --git a/src/wallet.h b/src/wallet.h
index 78f055a604..a7d07c993f 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -7,6 +7,7 @@
#include "bignum.h"
#include "key.h"
+#include "keystore.h"
#include "script.h"
class CWalletTx;
@@ -69,6 +70,8 @@ public:
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)
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 ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
@@ -114,15 +117,7 @@ public:
throw std::runtime_error("CWallet::GetCredit() : value out of range");
return (IsMine(txout) ? txout.nValue : 0);
}
- 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;
- }
+ bool IsChange(const CTxOut& txout) const;
int64 GetChange(const CTxOut& txout) const
{
if (!MoneyRange(txout.nValue))