aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2011-10-03 13:05:43 -0400
committerGavin Andresen <gavinandresen@gmail.com>2011-12-19 12:40:19 -0500
commite679ec969c8b22c676ebb10bea1038f6c8f13b33 (patch)
treec584f1a152c376ca0df93261d447c5b719449508
parentcc40ba2151a627b6da9af4932e0bee58e69aacb5 (diff)
OP_EVAL implementation
OP_EVAL is a new opcode that evaluates an item on the stack as a script. It enables a new type of bitcoin address that needs an arbitrarily complex script to redeem.
-rw-r--r--src/base58.h25
-rw-r--r--src/bitcoinrpc.cpp122
-rw-r--r--src/db.cpp9
-rw-r--r--src/db.h12
-rw-r--r--src/keystore.cpp30
-rw-r--r--src/keystore.h9
-rw-r--r--src/main.cpp230
-rw-r--r--src/main.h36
-rw-r--r--src/script.cpp656
-rw-r--r--src/script.h222
-rw-r--r--src/test/multisig_tests.cpp99
-rw-r--r--src/test/script_op_eval_tests.cpp203
-rw-r--r--src/test/script_tests.cpp40
-rw-r--r--src/wallet.cpp22
-rw-r--r--src/wallet.h12
15 files changed, 1155 insertions, 572 deletions
diff --git a/src/base58.h b/src/base58.h
index 113fa14a5e..8fc546c505 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -268,6 +268,12 @@ public:
SetHash160(Hash160(vchPubKey));
}
+ bool SetScriptHash160(const uint160& hash160)
+ {
+ SetData(fTestNet ? 112 : 1, &hash160, 20);
+ return true;
+ }
+
bool IsValid() const
{
int nExpectedSize = 20;
@@ -275,9 +281,20 @@ public:
switch(nVersion)
{
case 0:
+ nExpectedSize = 20; // Hash of public key
+ fExpectTestNet = false;
+ break;
+ case 1:
+ nExpectedSize = 20; // OP_EVAL, hash of CScript
+ fExpectTestNet = false;
break;
case 111:
+ nExpectedSize = 20;
+ fExpectTestNet = true;
+ break;
+ case 112:
+ nExpectedSize = 20;
fExpectTestNet = true;
break;
@@ -286,6 +303,14 @@ public:
}
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
}
+ bool IsScript() const
+ {
+ if (!IsValid())
+ return false;
+ if (fTestNet)
+ return nVersion == 112;
+ return nVersion == 1;
+ }
CBitcoinAddress()
{
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 122bf61b8f..3e42e70387 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -667,7 +667,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);
@@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex();
}
-Value sendmultisig(const Array& params, bool fHelp)
+Value addmultisigaddress(const Array& params, bool fHelp)
{
- if (fHelp || params.size() < 4 || params.size() > 7)
+ if (fHelp || params.size() < 2 || params.size() > 3)
{
- string msg = "sendmultisig <fromaccount> <type> <[\"key\",\"key\"]> <amount> [minconf=1] [comment] [comment-to]\n"
- "<type> is one of: \"and\", \"or\", \"escrow\"\n"
- "<keys> is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n"
- "<amount> is a real and is rounded to the nearest 0.00000001";
- if (pwalletMain->IsCrypted())
- msg += "\nrequires wallet passphrase to be set with walletpassphrase first";
+ 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);
}
- string strAccount = AccountFromValue(params[0]);
- string strType = params[1].get_str();
- const Array& keys = params[2].get_array();
- int64 nAmount = AmountFromValue(params[3]);
- int nMinDepth = 1;
- if (params.size() > 4)
- nMinDepth = params[4].get_int();
-
- CWalletTx wtx;
- wtx.strFromAccount = strAccount;
- if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
- wtx.mapValue["comment"] = params[5].get_str();
- if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty())
- wtx.mapValue["to"] = params[6].get_str();
-
- if (pwalletMain->IsLocked())
- throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
-
- // Check funds
- int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
- if (nAmount > nBalance)
- throw JSONRPCError(-6, "Account has insufficient funds");
+ 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
- int nKeysNeeded = 0;
- if (strType == "and" || strType == "or")
- nKeysNeeded = 2;
- else if (strType == "escrow")
- nKeysNeeded = 3;
- else
- throw runtime_error("sendmultisig: <type> must be one of: and or and_or");
- if (keys.size() != nKeysNeeded)
+ if (keys.size() < nRequired)
throw runtime_error(
- strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded));
+ strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
std::vector<CKey> pubkeys;
- pubkeys.resize(nKeysNeeded);
- for (int i = 0; i < nKeysNeeded; i++)
+ 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
@@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp)
CBitcoinAddress address(ks);
if (!pwalletMain->GetKey(address, pubkeys[i]))
throw runtime_error(
- strprintf("sendmultisig: unknown address: %s",ks.c_str()));
+ strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
}
}
- // Send
- CScript scriptPubKey;
- if (strType == "and")
- scriptPubKey.SetMultisigAnd(pubkeys);
- else if (strType == "or")
- scriptPubKey.SetMultisigOr(pubkeys);
- else
- scriptPubKey.SetMultisigEscrow(pubkeys);
+ // Construct using OP_EVAL
+ CScript inner;
+ inner.SetMultisig(nRequired, pubkeys);
- CReserveKey keyChange(pwalletMain);
- int64 nFeeRequired = 0;
- bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired);
- if (!fCreated)
- {
- if (nAmount + nFeeRequired > pwalletMain->GetBalance())
- throw JSONRPCError(-6, "Insufficient funds");
- throw JSONRPCError(-4, "Transaction creation failed");
- }
- if (!pwalletMain->CommitTransaction(wtx, keyChange))
- throw JSONRPCError(-4, "Transaction commit failed");
+ uint160 scriptHash = Hash160(inner);
+ CScript scriptPubKey;
+ scriptPubKey.SetEval(inner);
+ pwalletMain->AddCScript(scriptHash, inner);
+ CBitcoinAddress address;
+ address.SetScriptHash160(scriptHash);
- return wtx.GetHash().GetHex();
+ pwalletMain->SetAddressBookName(address, strAccount);
+ return address.ToString();
}
@@ -1700,6 +1665,24 @@ Value validateaddress(const Array& params, bool fHelp)
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;
+ txntype whichType;
+ int nRequired;
+ ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
+ ret.push_back(Pair("script", GetTxnTypeName(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))
@@ -1946,7 +1929,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany),
- make_pair("sendmultisig", &sendmultisig),
+ make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage),
@@ -2590,16 +2573,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 == "sendmultisig" && n > 2)
+ if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "addmultisigaddress" && n > 1)
{
- string s = params[2].get_str();
+ string s = params[1].get_str();
Value v;
if (!read_string(s, v) || v.type() != array_type)
- throw runtime_error("sendmultisig: type mismatch "+s);
- params[2] = v.get_array();
+ throw runtime_error("addmultisigaddress: type mismatch "+s);
+ params[1] = v.get_array();
}
- if (strMethod == "sendmultisig" && n > 3) ConvertTo<double>(params[3]);
- if (strMethod == "sendmultisig" && n > 4) ConvertTo<boost::int64_t>(params[4]);
// Execute
Object reply = CallRPC(strMethod, params);
diff --git a/src/db.cpp b/src/db.cpp
index 7195e065ea..52ad695c71 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;
+ std::vector<unsigned char> 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..99dd88b491 100644
--- a/src/db.h
+++ b/src/db.h
@@ -420,6 +420,18 @@ public:
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}
+ bool ReadCScript(const uint160 &hash, std::vector<unsigned char>& data)
+ {
+ data.clear();
+ return Read(std::make_pair(std::string("cscript"), hash), data);
+ }
+
+ bool WriteCScript(const uint160& hash, const std::vector<unsigned char>& data)
+ {
+ nWalletDBUpdated++;
+ return Write(std::make_pair(std::string("cscript"), hash), data, false);
+ }
+
bool WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 6cf557fafe..c9b9b4a5de 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -33,6 +33,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
return true;
}
+bool CBasicKeyStore::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
+{
+ CRITICAL_BLOCK(cs_KeyStore)
+ mapData[hash] = data;
+ return true;
+}
+
+bool CBasicKeyStore::HaveCScript(const uint160& hash) const
+{
+ bool result;
+ CRITICAL_BLOCK(cs_KeyStore)
+ result = (mapData.count(hash) > 0);
+ return result;
+}
+
+
+bool CBasicKeyStore::GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const
+{
+ CRITICAL_BLOCK(cs_KeyStore)
+ {
+ DataMap::const_iterator mi = mapData.find(hash);
+ if (mi != mapData.end())
+ {
+ dataOut = (*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..942fb9ae9c 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -31,6 +31,10 @@ public:
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
+ virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data) =0;
+ virtual bool HaveCScript(const uint160 &hash) const =0;
+ virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) 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 +48,14 @@ public:
};
typedef std::map<CBitcoinAddress, CSecret> KeyMap;
+typedef std::map<uint160, std::vector<unsigned char> > DataMap;
// Basic key store, that keeps keys in an address->secret map
class CBasicKeyStore : public CKeyStore
{
protected:
KeyMap mapKeys;
+ DataMap mapData;
public:
bool AddKey(const CKey& key);
@@ -86,6 +92,9 @@ public:
}
return false;
}
+ virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data);
+ virtual bool HaveCScript(const uint160 &hash) const;
+ virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) 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 052701e5fc..0fb383f593 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -246,6 +246,65 @@ 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 2-signature 2-of-3 escrow
+ // in an OP_EVAL, which is 2 ~80-byte signatures, 3
+ // ~65-byte public keys, plus a few script ops.
+ if (txin.scriptSig.size() > 400)
+ return error("nonstandard txin, size %d\n", txin.scriptSig.size());
+ 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;
+}
+
+//
+// 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::IsStandardInputs(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;
+ txntype whichType;
+ if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions))
+ return false;
+ if (whichType == TX_SCRIPTHASH)
+ {
+ vector<vector<unsigned char> > stack;
+ int nUnused;
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused))
+ return false;
+ const vector<unsigned char>& subscript = stack.back();
+ if (!::IsStandard(CScript(subscript.begin(), subscript.end())))
+ return false;
+ }
+ }
+
+ return true;
+}
+
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@@ -369,15 +428,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ((int64)nLockTime > 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 +471,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 (!IsStandardInputs(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 +895,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 +961,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()));
@@ -891,7 +979,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
// still computed and checked, and any change will be caught at the next checkpoint.
if (!(fBlock && IsInitialBlockDownload()))
// Verify signature
- if (!VerifySignature(txPrev, *this, i))
+ if (!VerifySignature(txPrev, *this, i, nSigOpsRet))
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
// Check for conflicts (double-spend)
@@ -965,7 +1053,8 @@ bool CTransaction::ClientConnectInputs()
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i))
+ int nUnused = 0;
+ if (!VerifySignature(txPrev, *this, i, nUnused))
return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@ -1023,14 +1112,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 +1387,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 +2936,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 +2944,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);
diff --git a/src/main.h b/src/main.h
index 7f8da4e898..bf31234c29 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 IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
int64 GetValueOut() const
{
@@ -640,8 +622,11 @@ 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);
+ 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 +836,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/script.cpp b/src/script.cpp
index 6a7913b0d5..c103d57510 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* GetTxnTypeName(txntype 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, 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,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}
break;
+ case OP_EVAL:
+ {
+ // 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
+ if (subscript.Find(OP_CODESEPARATOR))
+ return false;
+
+ if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType,
+ pbegincodehash, pendcodehash, nOpCount, nSigOpCount, nRecurseDepth++))
+ return false;
+ }
+ break;
+
default:
return false;
}
@@ -865,6 +1053,17 @@ 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, 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, 0);
+}
+
@@ -964,38 +1163,35 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
//
-// Returns lists of public keys (or public key hashes), any one of which can
-// satisfy scriptPubKey
+// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
-bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet)
+bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{
// Templates
- static vector<CScript> vTemplates;
- if (vTemplates.empty())
+ static map<txntype, 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);
-
- // Sender provides two pubkeys, receivers provides two signatures
- vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
+ mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
- // Sender provides two pubkeys, receivers provides one of two signatures
- vTemplates.push_back(CScript() << OP_1 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
+ // 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 three pubkeys, receiver provides 2 of 3 signatures.
- vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_PUBKEY << OP_3 << 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(txntype, CScript)& tplate, mTemplates)
{
+ const CScript& script2 = tplate.second;
vSolutionsRet.clear();
- vector<pair<opcodetype, valtype> > currentSolution;
opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2;
@@ -1006,218 +1202,333 @@ bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype>
{
if (pc1 == script1.end() && pc2 == script2.end())
{
- return !vSolutionsRet.empty();
+ // Found a match
+ typeRet = tplate.first;
+ if (typeRet == TX_MULTISIG)
+ {
+ // Additional checks for TX_MULTISIG:
+ unsigned char m = vSolutionsRet.front()[0];
+ unsigned char n = vSolutionsRet.back()[0];
+ if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
+ return false;
+ }
+ return true;
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
break;
+
+ // Template matching opcodes:
+ if (opcode2 == OP_PUBKEYS)
+ {
+ while (vch1.size() >= 33 && vch1.size() <= 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;
- currentSolution.push_back(make_pair(opcode2, vch1));
+ vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_PUBKEYHASH)
{
if (vch1.size() != sizeof(uint160))
break;
- currentSolution.push_back(make_pair(opcode2, vch1));
+ vSolutionsRet.push_back(vch1);
}
- else if (opcode2 == OP_CHECKSIG)
+ else if (opcode2 == OP_SCRIPTHASH)
{
- vSolutionsRet.push_back(currentSolution);
- currentSolution.clear();
+ if (vch1.size() != sizeof(uint160))
+ break;
+ vSolutionsRet.push_back(vch1);
}
- else if (opcode2 == OP_CHECKMULTISIG)
- { // Dig out the "m" from before the pubkeys:
- CScript::const_iterator it = script2.begin();
- opcodetype op_m;
- script2.GetOp(it, op_m, vch1);
- int m = CScript::DecodeOP_N(op_m);
- int n = currentSolution.size();
-
- if (m == 2 && n == 2)
+ else if (opcode2 == OP_SMALLINTEGER)
+ { // Single-byte small integer pushed onto vSolutions
+ if (opcode1 == OP_0 ||
+ (opcode1 >= OP_1 && opcode1 <= OP_16))
{
- vSolutionsRet.push_back(currentSolution);
- currentSolution.clear();
- }
- else if (m == 1 && n == 2)
- { // 2 solutions: either first key or second
- for (int i = 0; i < 2; i++)
- {
- vector<pair<opcodetype, valtype> > s;
- s.push_back(currentSolution[i]);
- vSolutionsRet.push_back(s);
- }
- currentSolution.clear();
- }
- else if (m == 2 && n == 3)
- { // 3 solutions: any pair
- for (int i = 0; i < 2; i++)
- for (int j = i+1; j < 3; j++)
- {
- vector<pair<opcodetype, valtype> > s;
- s.push_back(currentSolution[i]);
- s.push_back(currentSolution[j]);
- vSolutionsRet.push_back(s);
- }
- currentSolution.clear();
+ 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;
}
}
}
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<vector<pair<opcodetype, valtype> > > vSolutions;
- if (!Solver(scriptPubKey, vSolutions))
+ vector<unsigned char> vchSig;
+ if (!key.Sign(hash, vchSig))
return false;
+ vchSig.push_back((unsigned char)nHashType);
+ scriptSigRet << vchSig;
- // See if we have all the keys for any of the solutions:
- int whichSolution = -1;
- for (int i = 0; i < vSolutions.size(); i++)
- {
- int keysFound = 0;
- CScript scriptSig;
+ return true;
+}
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
- {
- if (item.first == OP_PUBKEY)
- {
- const valtype& vchPubKey = item.second;
- CKey key;
- vector<unsigned char> vchSig;
- if (keystore.GetKey(Hash160(vchPubKey), key) && key.GetPubKey() == vchPubKey
- && hash != 0 && key.Sign(hash, vchSig))
- {
- vchSig.push_back((unsigned char)nHashType);
- scriptSig << vchSig;
- ++keysFound;
- }
- }
- else if (item.first == OP_PUBKEYHASH)
- {
- CKey key;
- vector<unsigned char> vchSig;
- if (keystore.GetKey(uint160(item.second), key)
- && hash != 0 && key.Sign(hash, vchSig))
- {
- vchSig.push_back((unsigned char)nHashType);
- scriptSig << vchSig << key.GetPubKey();
- ++keysFound;
- }
- }
- }
- if (keysFound == vSolutions[i].size())
+bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
+{
+ int nSigned = 0;
+ int nRequired = multisigdata.front()[0];
+ for (vector<valtype>::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++)
+ {
+ const valtype& pubkey = *it;
+ CBitcoinAddress address;
+ address.SetPubKey(pubkey);
+ if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
{
- whichSolution = i;
- scriptSigRet = scriptSig;
- break;
+ ++nSigned;
+ if (nSigned == nRequired) break;
}
}
- if (whichSolution == -1)
+ 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();
+
+ txntype whichType;
+ vector<valtype> vSolutions;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
- // CHECKMULTISIG bug workaround:
- if (vSolutions.size() != 1 ||
- vSolutions[0].size() != 1)
+ CBitcoinAddress address;
+ valtype subscript;
+ switch (whichType)
{
- scriptSigRet.insert(scriptSigRet.begin(), OP_0);
+ 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
+ {
+ valtype vch;
+ keystore.GetPubKey(address, vch);
+ scriptSigRet << vch;
+ }
+ break;
+ case TX_SCRIPTHASH:
+ if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
+ return false;
+ if (!Solver(keystore, CScript(subscript.begin(), subscript.end()), hash, nHashType, scriptSigRet))
+ return false;
+ if (hash != 0)
+ scriptSigRet << 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<vector<pair<opcodetype, valtype> > > vSolutions;
- return Solver(scriptPubKey, vSolutions);
+ vector<valtype> vSolutions;
+ txntype 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<vector<pair<opcodetype, valtype> > > vSolutions;
- if (!Solver(scriptPubKey, vSolutions))
+ vector<valtype> vSolutions;
+ txntype whichType;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
- int keysFound = 0;
- int keysRequired = 0;
- for (int i = 0; i < vSolutions.size(); i++)
+ CBitcoinAddress address;
+ switch (whichType)
{
- BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
- {
- ++keysRequired;
- if (item.first == OP_PUBKEY)
- {
- const valtype& vchPubKey = item.second;
- vector<unsigned char> vchPubKeyFound;
- if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey)
- ++keysFound;
- }
- else if (item.first == OP_PUBKEYHASH)
- {
- if (keystore.HaveKey(uint160(item.second)))
- ++keysFound;
- }
- }
+ 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:
+ {
+ valtype subscript;
+ if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
+ return false;
+ return IsMine(keystore, CScript(subscript.begin(), subscript.end()));
}
-
- // 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
- // for shared-wallet situations.
- return (keysFound == keysRequired);
+ 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 ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
{
- vector<vector<pair<opcodetype, valtype> > > vSolutions;
- if (!Solver(scriptPubKey, vSolutions))
+ vector<valtype> vSolutions;
+ txntype whichType;
+ if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
- for (int i = 0; i < vSolutions.size(); i++)
+ if (whichType == TX_PUBKEY)
{
- if (vSolutions[i].size() != 1)
- continue; // Can't return more than one address...
-
- PAIRTYPE(opcodetype, valtype)& item = vSolutions[i][0];
- 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 ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txntype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
+{
+ 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 (vector<valtype>::const_iterator it = vSolutions.begin()+1; it != vSolutions.begin()+vSolutions.size()-1; it++)
+ {
+ CBitcoinAddress address;
+ address.SetPubKey(*it);
+ addressRet.push_back(address);
+ }
+ }
+ else
+ {
+ 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);
+ }
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
+ return true;
+}
+
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
{
vector<vector<unsigned char> > stack;
- if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet))
return false;
- if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
+ if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, 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 (scriptSig.Find(OP_EVAL)+scriptPubKey.Find(OP_EVAL) > 0)
+ {
+ int nUnused = 0;
+ stack.clear();
+ CScript sigCopy = scriptSig;
+ sigCopy.FindAndDelete(CScript(OP_EVAL));
+ CScript pubKeyCopy = scriptPubKey;
+ pubKeyCopy.FindAndDelete(CScript(OP_EVAL));
+
+ if (!EvalScript(stack, sigCopy, txTo, nIn, nHashType, nUnused))
+ return false;
+ if (!EvalScript(stack, pubKeyCopy, txTo, nIn, nHashType, nUnused))
+ return false;
+ if (stack.empty())
+ return false;
+ if (fResult != CastToBool(stack.back()))
+ return false;
+ }
+
+ return fResult;
}
@@ -1238,15 +1549,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))
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)
{
assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn];
@@ -1257,27 +1569,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))
return false;
return true;
}
-void CScript::SetMultisigAnd(const std::vector<CKey>& keys)
+void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
{
- assert(keys.size() >= 2);
this->clear();
- *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+ 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::SetMultisigOr(const std::vector<CKey>& keys)
+
+void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
{
- assert(keys.size() >= 2);
this->clear();
- *this << OP_1 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
+
+ *this << EncodeOP_N(nRequired);
+ BOOST_FOREACH(const CKey& key, keys)
+ *this << key.GetPubKey();
+ *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
}
-void CScript::SetMultisigEscrow(const std::vector<CKey>& keys)
+
+void CScript::SetEval(const CScript& subscript)
{
- assert(keys.size() >= 3);
+ assert(!subscript.empty());
+ uint160 subscriptHash = Hash160(subscript);
this->clear();
- *this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << keys[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
+ *this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL;
}
diff --git a/src/script.h b/src/script.h
index a5a1e1868c..ee0be02a82 100644
--- a/src/script.h
+++ b/src/script.h
@@ -24,6 +24,17 @@ enum
};
+enum txntype
+{
+ TX_NONSTANDARD,
+ // 'standard' transaction types:
+ TX_PUBKEY,
+ TX_PUBKEYHASH,
+ TX_SCRIPTHASH,
+ TX_MULTISIG,
+};
+
+const char* GetTxnTypeName(txntype 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,6 +441,7 @@ public:
return true;
}
+ // Encode/decode small integers:
static int DecodeOP_N(opcodetype opcode)
{
if (opcode == OP_0)
@@ -581,22 +449,44 @@ public:
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;
@@ -614,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())
{
@@ -632,19 +520,13 @@ public:
}
- 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 SetMultisigAnd(const std::vector<CKey>& keys);
- void SetMultisigOr(const std::vector<CKey>& keys);
- void SetMultisigEscrow(const std::vector<CKey>& keys);
+ void SetMultisig(int nRequired, const std::vector<CKey>& keys);
+ void SetEval(const CScript& subscript);
void PrintHex() const
@@ -685,14 +567,14 @@ public:
+bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, 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, txntype& 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, txntype& 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);
#endif
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index 459d112369..75c764dd65 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -20,9 +20,7 @@ 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 nHashType);
-extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
-extern bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet);
+extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, int nHashType);
BOOST_AUTO_TEST_SUITE(multisig_tests)
@@ -76,24 +74,25 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
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, 0));
+ BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0));
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, 0), strprintf("a&b 1: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), 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, 0), strprintf("a&b 2: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 2: %d", i));
}
// Test a OR b:
@@ -103,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
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, 0), strprintf("a|b: %d", i));
+ BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
else
- BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
}
s.clear();
s << OP_0 << OP_0;
- BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
+ BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
s.clear();
s << OP_0 << OP_1;
- BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
+ BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
for (int i = 0; i < 4; i++)
@@ -122,16 +121,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
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, 0), strprintf("escrow 1: %d %d", i, j));
+ BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 1: %d %d", i, j));
else
- BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 2: %d %d", i, j));
+ BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 2: %d %d", i, j));
}
}
BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{
- CKey key[3];
- for (int i = 0; i < 3; i++)
+ CKey key[4];
+ for (int i = 0; i < 4; i++)
key[i].MakeNewKey();
CScript a_and_b;
@@ -145,6 +144,21 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
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)
@@ -170,13 +184,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}
{
- vector<vector<pair<opcodetype, valtype> > > solutions;
+ vector<valtype> solutions;
+ txntype whichType;
CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG;
- BOOST_CHECK(Solver(s, solutions));
+ BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
- if (solutions.size() == 1)
- BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -184,13 +197,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
- vector<vector<pair<opcodetype, valtype> > > solutions;
+ vector<valtype> solutions;
+ txntype whichType;
CScript s;
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK(Solver(s, solutions));
+ BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
- if (solutions.size() == 1)
- BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -198,47 +210,40 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
- vector<vector<pair<opcodetype, valtype> > > solutions;
+ vector<valtype> solutions;
+ txntype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, solutions));
- BOOST_CHECK(solutions.size() == 1);
- if (solutions.size() == 1)
- BOOST_CHECK(solutions[0].size() == 2);
+ 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<vector<pair<opcodetype, valtype> > > solutions;
+ vector<valtype> solutions;
+ txntype whichType;
CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, solutions));
- BOOST_CHECK(solutions.size() == 2);
- if (solutions.size() == 2)
- {
- BOOST_CHECK(solutions[0].size() == 1);
- BOOST_CHECK(solutions[1].size() == 1);
- }
- CBitcoinAddress addr;
- BOOST_CHECK(ExtractAddress(s, &keystore, addr));
- BOOST_CHECK(addr == keyaddr[0]);
+ 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<vector<pair<opcodetype, valtype> > > solutions;
+ vector<valtype> solutions;
+ txntype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
- BOOST_CHECK(Solver(s, solutions));
- BOOST_CHECK(solutions.size() == 3);
- if (solutions.size() == 3)
- {
- BOOST_CHECK(solutions[0].size() == 2);
- BOOST_CHECK(solutions[1].size() == 2);
- BOOST_CHECK(solutions[2].size() == 2);
- }
+ BOOST_CHECK(Solver(s, whichType, solutions));
+ BOOST_CHECK(solutions.size() == 5);
}
}
diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp
new file mode 100644
index 0000000000..857d04bc6d
--- /dev/null
+++ b/src/test/script_op_eval_tests.cpp
@@ -0,0 +1,203 @@
+#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);
+
+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));
+ BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
+}
+
+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_backcompat)
+{
+ // Check backwards-incompatibility-testing code
+ CScript returnsEleven;
+ returnsEleven << OP_11;
+
+ // This will validate on new clients, but will
+ // be invalid on old clients (that interpret OP_EVAL as a no-op)
+ 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));
+ BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index f3fa5c3a1b..3d1c218700 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -7,7 +7,7 @@
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 VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType);
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
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, 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, 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, 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, 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));
txTo12.vout[0].nValue = 2;
- BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0));
+ BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
- BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, 0));
+ BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0));
CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
- BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0));
+ BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
}
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));
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));
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));
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));
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));
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));
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));
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));
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));
}
diff --git a/src/wallet.cpp b/src/wallet.cpp
index a662111d49..5d44b1fb8d 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 std::vector<unsigned char>& data)
+{
+ if (!CCryptoKeyStore::AddCScript(hash, data))
+ return false;
+ if (!fFileBacked)
+ return true;
+ return CWalletDB(strWalletFile).WriteCScript(hash, data);
+}
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
if (!IsLocked())
@@ -374,6 +383,16 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
return 0;
}
+bool CWallet::IsChange(const CTxOut& txout) const
+{
+ CBitcoinAddress address;
+ if (ExtractAddress(txout.scriptPubKey, this, address) && !address.IsScript())
+ CRITICAL_BLOCK(cs_wallet)
+ if (!mapAddressBook.count(address))
+ return true;
+ return false;
+}
+
int64 CWalletTx::GetTxTime() const
{
return nTimeReceived;
@@ -443,8 +462,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;
diff --git a/src/wallet.h b/src/wallet.h
index 78f055a604..34090ec803 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -69,6 +69,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 std::vector<unsigned char>& data);
+ bool LoadCScript(const uint160& hash, const std::vector<unsigned char>& data) { return CCryptoKeyStore::AddCScript(hash, data); }
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
@@ -114,15 +116,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))