Browse Source

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.
miguelfreitas
Gavin Andresen 13 years ago
parent
commit
e679ec969c
  1. 25
      src/base58.h
  2. 122
      src/bitcoinrpc.cpp
  3. 9
      src/db.cpp
  4. 12
      src/db.h
  5. 30
      src/keystore.cpp
  6. 9
      src/keystore.h
  7. 182
      src/main.cpp
  8. 36
      src/main.h
  9. 634
      src/script.cpp
  10. 222
      src/script.h
  11. 99
      src/test/multisig_tests.cpp
  12. 203
      src/test/script_op_eval_tests.cpp
  13. 40
      src/test/script_tests.cpp
  14. 22
      src/wallet.cpp
  15. 12
      src/wallet.h

25
src/base58.h

@ -268,6 +268,12 @@ public:
SetHash160(Hash160(vchPubKey)); SetHash160(Hash160(vchPubKey));
} }
bool SetScriptHash160(const uint160& hash160)
{
SetData(fTestNet ? 112 : 1, &hash160, 20);
return true;
}
bool IsValid() const bool IsValid() const
{ {
int nExpectedSize = 20; int nExpectedSize = 20;
@ -275,9 +281,20 @@ public:
switch(nVersion) switch(nVersion)
{ {
case 0: case 0:
nExpectedSize = 20; // Hash of public key
fExpectTestNet = false;
break;
case 1:
nExpectedSize = 20; // OP_EVAL, hash of CScript
fExpectTestNet = false;
break; break;
case 111: case 111:
nExpectedSize = 20;
fExpectTestNet = true;
break;
case 112:
nExpectedSize = 20;
fExpectTestNet = true; fExpectTestNet = true;
break; break;
@ -286,6 +303,14 @@ public:
} }
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
} }
bool IsScript() const
{
if (!IsValid())
return false;
if (fTestNet)
return nVersion == 112;
return nVersion == 1;
}
CBitcoinAddress() CBitcoinAddress()
{ {

122
src/bitcoinrpc.cpp

@ -667,7 +667,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
if (params.size() > 1) if (params.size() > 1)
nMinDepth = params[1].get_int(); nMinDepth = params[1].get_int();
// Get the set of pub keys that have the label // Get the set of pub keys assigned to account
string strAccount = AccountFromValue(params[0]); string strAccount = AccountFromValue(params[0]);
set<CBitcoinAddress> setAddress; set<CBitcoinAddress> setAddress;
GetAccountAddresses(strAccount, setAddress); GetAccountAddresses(strAccount, setAddress);
@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex(); 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" string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
"<type> is one of: \"and\", \"or\", \"escrow\"\n" "Add a nrequired-to-sign multisignature address to the wallet\"\n"
"<keys> is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n" "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 [account] is specified, assign address to [account].";
if (pwalletMain->IsCrypted())
msg += "\nrequires wallet passphrase to be set with walletpassphrase first";
throw runtime_error(msg); throw runtime_error(msg);
} }
string strAccount = AccountFromValue(params[0]); int nRequired = params[0].get_int();
string strType = params[1].get_str(); const Array& keys = params[1].get_array();
const Array& keys = params[2].get_array(); string strAccount;
int64 nAmount = AmountFromValue(params[3]); if (params.size() > 2)
int nMinDepth = 1; strAccount = AccountFromValue(params[2]);
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");
// Gather public keys // Gather public keys
int nKeysNeeded = 0; if (keys.size() < nRequired)
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)
throw runtime_error( 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; std::vector<CKey> pubkeys;
pubkeys.resize(nKeysNeeded); pubkeys.resize(keys.size());
for (int i = 0; i < nKeysNeeded; i++) for (int i = 0; i < keys.size(); i++)
{ {
const std::string& ks = keys[i].get_str(); const std::string& ks = keys[i].get_str();
if (ks.size() == 130) // hex public key if (ks.size() == 130) // hex public key
@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp)
CBitcoinAddress address(ks); CBitcoinAddress address(ks);
if (!pwalletMain->GetKey(address, pubkeys[i])) if (!pwalletMain->GetKey(address, pubkeys[i]))
throw runtime_error( throw runtime_error(
strprintf("sendmultisig: unknown address: %s",ks.c_str())); strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
} }
} }
// Send // Construct using OP_EVAL
CScript scriptPubKey; CScript inner;
if (strType == "and") inner.SetMultisig(nRequired, pubkeys);
scriptPubKey.SetMultisigAnd(pubkeys);
else if (strType == "or")
scriptPubKey.SetMultisigOr(pubkeys);
else
scriptPubKey.SetMultisigEscrow(pubkeys);
CReserveKey keyChange(pwalletMain); uint160 scriptHash = Hash160(inner);
int64 nFeeRequired = 0; CScript scriptPubKey;
bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired); scriptPubKey.SetEval(inner);
if (!fCreated) pwalletMain->AddCScript(scriptHash, inner);
{ CBitcoinAddress address;
if (nAmount + nFeeRequired > pwalletMain->GetBalance()) address.SetScriptHash160(scriptHash);
throw JSONRPCError(-6, "Insufficient funds");
throw JSONRPCError(-4, "Transaction creation failed");
}
if (!pwalletMain->CommitTransaction(wtx, keyChange))
throw JSONRPCError(-4, "Transaction commit failed");
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()); std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); 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 else
ret.push_back(Pair("ismine", false)); ret.push_back(Pair("ismine", false));
if (pwalletMain->mapAddressBook.count(address)) if (pwalletMain->mapAddressBook.count(address))
@ -1946,7 +1929,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd), make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom), make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany), make_pair("sendmany", &sendmany),
make_pair("sendmultisig", &sendmultisig), make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction), make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions), make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage), make_pair("signmessage", &signmessage),
@ -2590,16 +2573,15 @@ int CommandLineRPC(int argc, char *argv[])
params[1] = v.get_obj(); params[1] = v.get_obj();
} }
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "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; Value v;
if (!read_string(s, v) || v.type() != array_type) if (!read_string(s, v) || v.type() != array_type)
throw runtime_error("sendmultisig: type mismatch "+s); throw runtime_error("addmultisigaddress: type mismatch "+s);
params[2] = v.get_array(); 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 // Execute
Object reply = CallRPC(strMethod, params); Object reply = CallRPC(strMethod, params);

9
src/db.cpp

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

12
src/db.h

@ -420,6 +420,18 @@ public:
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); 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) bool WriteBestBlock(const CBlockLocator& locator)
{ {
nWalletDBUpdated++; nWalletDBUpdated++;

30
src/keystore.cpp

@ -33,6 +33,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
return true; 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() bool CCryptoKeyStore::SetCrypted()
{ {
CRITICAL_BLOCK(cs_KeyStore) CRITICAL_BLOCK(cs_KeyStore)

9
src/keystore.h

@ -31,6 +31,10 @@ public:
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0; virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
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 // Generate a new key, and add it to the store
virtual std::vector<unsigned char> GenerateNewKey(); virtual std::vector<unsigned char> GenerateNewKey();
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
@ -44,12 +48,14 @@ public:
}; };
typedef std::map<CBitcoinAddress, CSecret> KeyMap; 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 // Basic key store, that keeps keys in an address->secret map
class CBasicKeyStore : public CKeyStore class CBasicKeyStore : public CKeyStore
{ {
protected: protected:
KeyMap mapKeys; KeyMap mapKeys;
DataMap mapData;
public: public:
bool AddKey(const CKey& key); bool AddKey(const CKey& key);
@ -86,6 +92,9 @@ public:
} }
return false; 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; typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;

182
src/main.cpp

@ -246,6 +246,65 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
return ReadFromDisk(txdb, prevout, txindex); return ReadFromDisk(txdb, prevout, txindex);
} }
bool CTransaction::IsStandard() const
{
BOOST_FOREACH(const CTxIn& txin, vin)
{
// Biggest 'standard' txin is a 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) int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@ -369,15 +428,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ((int64)nLockTime > INT_MAX) if ((int64)nLockTime > INT_MAX)
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
// Safety limits
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 34 bytes.
// 34 bytes because a TxOut is:
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
if (GetSigOpCount() > nSize / 34 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
// Rather not work on nonstandard transactions (unless -testnet) // Rather not work on nonstandard transactions (unless -testnet)
if (!fTestNet && !IsStandard()) if (!fTestNet && !IsStandard())
return error("AcceptToMemoryPool() : nonstandard transaction type"); return error("AcceptToMemoryPool() : nonstandard transaction type");
@ -421,15 +471,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if (fCheckInputs) if (fCheckInputs)
{ {
// Check against previous transactions map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
map<uint256, CTxIndex> mapUnused; map<uint256, CTxIndex> mapUnused;
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
{
if (pfMissingInputs)
*pfMissingInputs = true;
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
}
// Check for non-standard OP_EVALs in inputs
if (!IsStandardInputs(mapInputs))
return error("AcceptToMemoryPool() : nonstandard transaction input");
// Check against previous transactions
int64 nFees = 0; int64 nFees = 0;
if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) int nSigOps = 0;
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
{ {
if (pfMissingInputs) if (pfMissingInputs)
*pfMissingInputs = true; *pfMissingInputs = true;
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
} }
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 65 bytes.
// 65 bytes because that is the minimum size of an ECDSA signature
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
if (nSigOps > nSize / 65 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
// Don't accept it if it can't get into a block // Don't accept it if it can't get into a block
if (nFees < GetMinFee(1000, true, true)) if (nFees < GetMinFee(1000, true, true))
@ -826,27 +895,25 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
} }
bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx, bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
{ {
// Take over previous transactions' spent pointers if (IsCoinBase())
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain return true; // Coinbase transactions have no inputs to fetch.
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
if (!IsCoinBase())
{
int64 nValueIn = 0;
for (int i = 0; i < vin.size(); i++) for (int i = 0; i < vin.size(); i++)
{ {
COutPoint prevout = vin[i].prevout; COutPoint prevout = vin[i].prevout;
if (inputsRet.count(prevout.hash))
continue; // Got it already
// Read txindex // Read txindex
CTxIndex txindex; CTxIndex& txindex = inputsRet[prevout.hash].first;
bool fFound = true; bool fFound = true;
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
{ {
// Get txindex from current proposed changes // Get txindex from current proposed changes
txindex = mapTestPool[prevout.hash]; txindex = mapTestPool.find(prevout.hash)->second;
} }
else else
{ {
@ -854,17 +921,17 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
fFound = txdb.ReadTxIndex(prevout.hash, txindex); fFound = txdb.ReadTxIndex(prevout.hash, txindex);
} }
if (!fFound && (fBlock || fMiner)) if (!fFound && (fBlock || fMiner))
return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
// Read txPrev // Read txPrev
CTransaction txPrev; CTransaction& txPrev = inputsRet[prevout.hash].second;
if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
{ {
// Get prev tx from single transactions in memory // Get prev tx from single transactions in memory
CRITICAL_BLOCK(cs_mapTransactions) CRITICAL_BLOCK(cs_mapTransactions)
{ {
if (!mapTransactions.count(prevout.hash)) if (!mapTransactions.count(prevout.hash))
return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
txPrev = mapTransactions[prevout.hash]; txPrev = mapTransactions[prevout.hash];
} }
if (!fFound) if (!fFound)
@ -874,8 +941,29 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
{ {
// Get prev tx from disk // Get prev tx from disk
if (!txPrev.ReadFromDisk(txindex.pos)) if (!txPrev.ReadFromDisk(txindex.pos))
return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
}
} }
return true;
}
bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
// fMiner is true when called from the internal bitcoin miner
// ... both are false when called from CTransaction::AcceptToMemoryPool
if (!IsCoinBase())
{
int64 nValueIn = 0;
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
assert(inputs.count(prevout.hash) > 0);
CTxIndex& txindex = inputs[prevout.hash].first;
CTransaction& txPrev = inputs[prevout.hash].second;
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
@ -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. // still computed and checked, and any change will be caught at the next checkpoint.
if (!(fBlock && IsInitialBlockDownload())) if (!(fBlock && IsInitialBlockDownload()))
// Verify signature // 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())); return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
// Check for conflicts (double-spend) // Check for conflicts (double-spend)
@ -965,7 +1053,8 @@ bool CTransaction::ClientConnectInputs()
return false; return false;
// Verify signature // Verify signature
if (!VerifySignature(txPrev, *this, i)) int nUnused = 0;
if (!VerifySignature(txPrev, *this, i, nUnused))
return error("ConnectInputs() : VerifySignature failed"); return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@ -1023,14 +1112,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
map<uint256, CTxIndex> mapQueuedChanges; map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0; int64 nFees = 0;
int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx) BOOST_FOREACH(CTransaction& tx, vtx)
{ {
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
nTxPos += ::GetSerializeSize(tx, SER_DISK); nTxPos += ::GetSerializeSize(tx, SER_DISK);
if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false)) map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
return false; 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 // Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{ {
@ -1291,8 +1387,21 @@ bool CBlock::CheckBlock() const
if (!tx.CheckTransaction()) if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
// Check that it's not full of nonstandard transactions // This code should be removed when a compatibility-breaking block chain split has passed.
if (GetSigOpCount() > MAX_BLOCK_SIGOPS) // Compatibility check for old clients that counted sigops differently:
int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount();
}
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount();
}
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkleroot // Check merkleroot
@ -2827,9 +2936,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue; continue;
int nTxSigOps = tx.GetSigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
// Transaction fee required depends on block size // Transaction fee required depends on block size
bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
@ -2838,7 +2944,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Connecting shouldn't fail due to dependency on other memory pool transactions // Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency // because we're already processing them in order of dependency
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool); map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
continue;
int nTxSigOps = 0;
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
continue;
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue; continue;
swap(mapTestPool, mapTestPoolTmp); swap(mapTestPool, mapTestPoolTmp);

36
src/main.h

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

634
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; CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin(); CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end(); CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode; opcodetype opcode;
valtype vchPushValue; valtype vchPushValue;
vector<bool> vfExec; vector<bool> vfExec;
vector<valtype> altstack; vector<valtype> altstack;
if (script.size() > 10000) if (script.size() > 10000)
return false; return false;
int nOpCount = 0;
// Limit OP_EVAL recursion
if (nRecurseDepth > 2)
return false;
try try
{ {
@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Control // Control
// //
case OP_NOP: case OP_NOP:
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
break; break;
@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend); CScript scriptCode(pbegincodehash, pendcodehash);
// Drop the signature, since there's no way for a signature to sign itself // Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig)); scriptCode.FindAndDelete(CScript(vchSig));
bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
nSigOpCount++;
popstack(stack); popstack(stack);
popstack(stack); popstack(stack);
@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return false; return false;
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend); CScript scriptCode(pbegincodehash, pendcodehash);
// Drop the signatures, since there's no way for a signature to sign itself // Drop the signatures, since there's no way for a signature to sign itself
for (int k = 0; k < nSigsCount; k++) for (int k = 0; k < nSigsCount; k++)
@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
} }
ikey++; ikey++;
nKeysCount--; nKeysCount--;
nSigOpCount++;
// If there are more signatures left than keys left, // If there are more signatures left than keys left,
// then too many signatures have failed // then too many signatures have failed
@ -844,6 +1012,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
} }
break; 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: default:
return false; return false;
} }
@ -865,6 +1053,17 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return true; return true;
} }
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script,
const CTransaction& txTo, unsigned int nIn, int nHashType, 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 // Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
// satisfy scriptPubKey
// //
bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet) bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{ {
// Templates // Templates
static vector<CScript> vTemplates; static map<txntype, CScript> mTemplates;
if (vTemplates.empty()) if (mTemplates.empty())
{ {
// Standard tx, sender provides pubkey, receiver adds signature // Standard tx, sender provides pubkey, receiver adds signature
vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
// Sender provides two pubkeys, receivers provides two signatures
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
// Sender provides two pubkeys, receivers provides one of two signatures // Sender provides N pubkeys, receivers provides M signatures
vTemplates.push_back(CScript() << OP_1 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG); 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. // Sender provides script hash, receiver provides script and
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_PUBKEY << OP_3 << OP_CHECKMULTISIG); // as many signatures as required to satisfy script
mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL));
} }
// Scan templates // Scan templates
const CScript& script1 = scriptPubKey; const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const CScript& script2, vTemplates) BOOST_FOREACH(const PAIRTYPE(txntype, CScript)& tplate, mTemplates)
{ {
const CScript& script2 = tplate.second;
vSolutionsRet.clear(); vSolutionsRet.clear();
vector<pair<opcodetype, valtype> > currentSolution;
opcodetype opcode1, opcode2; opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2; 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()) 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)) if (!script1.GetOp(pc1, opcode1, vch1))
break; break;
}
if (!script2.GetOp(pc2, opcode2, vch2)) if (!script2.GetOp(pc2, opcode2, vch2))
break; break;
// Normal situation is to fall through
// to other if/else statments
}
if (opcode2 == OP_PUBKEY) if (opcode2 == OP_PUBKEY)
{ {
if (vch1.size() < 33 || vch1.size() > 120) if (vch1.size() < 33 || vch1.size() > 120)
break; break;
currentSolution.push_back(make_pair(opcode2, vch1)); vSolutionsRet.push_back(vch1);
} }
else if (opcode2 == OP_PUBKEYHASH) else if (opcode2 == OP_PUBKEYHASH)
{ {
if (vch1.size() != sizeof(uint160)) if (vch1.size() != sizeof(uint160))
break; break;
currentSolution.push_back(make_pair(opcode2, vch1)); vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_CHECKSIG)
{
vSolutionsRet.push_back(currentSolution);
currentSolution.clear();
}
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)
{
vSolutionsRet.push_back(currentSolution);
currentSolution.clear();
} }
else if (m == 1 && n == 2) else if (opcode2 == OP_SCRIPTHASH)
{ // 2 solutions: either first key or second
for (int i = 0; i < 2; i++)
{ {
vector<pair<opcodetype, valtype> > s; if (vch1.size() != sizeof(uint160))
s.push_back(currentSolution[i]); break;
vSolutionsRet.push_back(s); vSolutionsRet.push_back(vch1);
}
currentSolution.clear();
} }
else if (m == 2 && n == 3) else if (opcode2 == OP_SMALLINTEGER)
{ // 3 solutions: any pair { // Single-byte small integer pushed onto vSolutions
for (int i = 0; i < 2; i++) if (opcode1 == OP_0 ||
for (int j = i+1; j < 3; j++) (opcode1 >= OP_1 && opcode1 <= OP_16))
{ {
vector<pair<opcodetype, valtype> > s; char n = (char)CScript::DecodeOP_N(opcode1);
s.push_back(currentSolution[i]); vSolutionsRet.push_back(valtype(1, n));
s.push_back(currentSolution[j]);
vSolutionsRet.push_back(s);
}
currentSolution.clear();
} }
else
break;
} }
else if (opcode1 != opcode2 || vch1 != vch2) else if (opcode1 != opcode2 || vch1 != vch2)
{ {
// Others must match exactly
break; break;
} }
} }
} }
vSolutionsRet.clear(); vSolutionsRet.clear();
typeRet = TX_NONSTANDARD;
return false; return false;
} }
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
{ {
scriptSigRet.clear(); CKey key;
if (!keystore.GetKey(address, key))
return false;
vector<vector<pair<opcodetype, valtype> > > vSolutions; vector<unsigned char> vchSig;
if (!Solver(scriptPubKey, vSolutions)) if (!key.Sign(hash, vchSig))
return false; return false;
vchSig.push_back((unsigned char)nHashType);
scriptSigRet << vchSig;
// See if we have all the keys for any of the solutions: return true;
int whichSolution = -1; }
for (int i = 0; i < vSolutions.size(); i++)
{
int keysFound = 0;
CScript scriptSig;
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i]) 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) const valtype& pubkey = *it;
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
{ {
const valtype& vchPubKey = item.second; ++nSigned;
CKey key; if (nSigned == nRequired) break;
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) 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;
CBitcoinAddress address;
valtype subscript;
switch (whichType)
{ {
CKey key; case TX_NONSTANDARD:
vector<unsigned char> vchSig; return false;
if (keystore.GetKey(uint160(item.second), key) case TX_PUBKEY:
&& hash != 0 && key.Sign(hash, vchSig)) 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
{ {
vchSig.push_back((unsigned char)nHashType); valtype vch;
scriptSig << vchSig << key.GetPubKey(); keystore.GetPubKey(address, vch);
++keysFound; scriptSigRet << vch;
}
} }
}
if (keysFound == vSolutions[i].size())
{
whichSolution = i;
scriptSigRet = scriptSig;
break; 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;
if (whichSolution == -1) }
bool IsStandard(const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
// CHECKMULTISIG bug workaround: if (whichType == TX_MULTISIG)
if (vSolutions.size() != 1 ||
vSolutions[0].size() != 1)
{ {
scriptSigRet.insert(scriptSigRet.begin(), OP_0); 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 true; return whichType != TX_NONSTANDARD;
} }
bool IsStandard(const CScript& scriptPubKey) int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
{ {
vector<vector<pair<opcodetype, valtype> > > vSolutions; int nResult = 0;
return Solver(scriptPubKey, vSolutions); BOOST_FOREACH(const valtype& pubkey, pubkeys)
{
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (keystore.HaveKey(address))
++nResult;
}
return nResult;
} }
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{ {
vector<vector<pair<opcodetype, valtype> > > vSolutions; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, vSolutions)) txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
int keysFound = 0; CBitcoinAddress address;
int keysRequired = 0; switch (whichType)
for (int i = 0; i < vSolutions.size(); i++)
{
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
{ {
++keysRequired; case TX_NONSTANDARD:
if (item.first == OP_PUBKEY) 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:
{ {
const valtype& vchPubKey = item.second; valtype subscript;
vector<unsigned char> vchPubKeyFound; if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey) return false;
++keysFound; return IsMine(keystore, CScript(subscript.begin(), subscript.end()));
} }
else if (item.first == OP_PUBKEYHASH) case TX_MULTISIG:
{ {
if (keystore.HaveKey(uint160(item.second)))
++keysFound;
}
}
}
// Only consider transactions "mine" if we own ALL the // Only consider transactions "mine" if we own ALL the
// keys involved. multi-signature transactions that are // keys involved. multi-signature transactions that are
// partially owned (somebody else has a key that can spend // partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially // them) enable spend-out-from-under-you attacks, especially
// for shared-wallet situations. // in shared-wallet situations.
return (keysFound == keysRequired); 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) bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
{ {
vector<vector<pair<opcodetype, valtype> > > vSolutions; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, vSolutions)) txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
for (int i = 0; i < vSolutions.size(); i++) if (whichType == TX_PUBKEY)
{ {
if (vSolutions[i].size() != 1) addressRet.SetPubKey(vSolutions[0]);
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; return true;
} }
else if (whichType == TX_PUBKEYHASH)
{
addressRet.SetHash160(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
addressRet.SetScriptHash160(uint160(vSolutions[0]));
return true;
}
// Multisig txns have more than one address...
return false; return false;
} }
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, 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);
}
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)
{ {
vector<vector<unsigned char> > stack; vector<vector<unsigned char> > stack;
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet))
return false; return false;
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, nSigOpCountRet))
return false; return false;
if (stack.empty()) if (stack.empty())
return false; return false;
return CastToBool(stack.back()); bool fResult = CastToBool(stack.back());
// This code should be removed when a compatibility-breaking block chain split has passed.
// Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains
// OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op:
if (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; txin.scriptSig = scriptPrereq + txin.scriptSig;
// Test solution // Test solution
int nUnused = 0;
if (scriptPrereq.empty()) if (scriptPrereq.empty())
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0))
return false; return false;
return true; return true;
} }
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
{ {
assert(nIn < txTo.vin.size()); assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn]; 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()) if (txin.prevout.hash != txFrom.GetHash())
return false; return false;
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType))
return false; return false;
return true; return true;
} }
void CScript::SetMultisigAnd(const std::vector<CKey>& keys) void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
{ {
assert(keys.size() >= 2);
this->clear(); 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->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->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;
} }

222
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 enum opcodetype
{ {
@ -147,8 +158,10 @@ enum opcodetype
OP_CHECKMULTISIG, OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY, OP_CHECKMULTISIGVERIFY,
// meta
OP_EVAL, // Was OP_NOP1
// expansion // expansion
OP_NOP1,
OP_NOP2, OP_NOP2,
OP_NOP3, OP_NOP3,
OP_NOP4, OP_NOP4,
@ -162,162 +175,16 @@ enum opcodetype
// template matching params // template matching params
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_SCRIPTHASH = 0xfc,
OP_PUBKEYHASH = 0xfd, OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe, OP_PUBKEY = 0xfe,
OP_INVALIDOPCODE = 0xff, OP_INVALIDOPCODE = 0xff,
}; };
const char* GetOpName(opcodetype opcode);
inline const char* GetOpName(opcodetype opcode)
{
switch (opcode)
{
// push value
case OP_0 : return "0";
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
case OP_1NEGATE : return "-1";
case OP_RESERVED : return "OP_RESERVED";
case OP_1 : return "1";
case OP_2 : return "2";
case OP_3 : return "3";
case OP_4 : return "4";
case OP_5 : return "5";
case OP_6 : return "6";
case OP_7 : return "7";
case OP_8 : return "8";
case OP_9 : return "9";
case OP_10 : return "10";
case OP_11 : return "11";
case OP_12 : return "12";
case OP_13 : return "13";
case OP_14 : return "14";
case OP_15 : return "15";
case OP_16 : return "16";
// control
case OP_NOP : return "OP_NOP";
case OP_VER : return "OP_VER";
case OP_IF : return "OP_IF";
case OP_NOTIF : return "OP_NOTIF";
case OP_VERIF : return "OP_VERIF";
case OP_VERNOTIF : return "OP_VERNOTIF";
case OP_ELSE : return "OP_ELSE";
case OP_ENDIF : return "OP_ENDIF";
case OP_VERIFY : return "OP_VERIFY";
case OP_RETURN : return "OP_RETURN";
// stack ops
case OP_TOALTSTACK : return "OP_TOALTSTACK";
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
case OP_2DROP : return "OP_2DROP";
case OP_2DUP : return "OP_2DUP";
case OP_3DUP : return "OP_3DUP";
case OP_2OVER : return "OP_2OVER";
case OP_2ROT : return "OP_2ROT";
case OP_2SWAP : return "OP_2SWAP";
case OP_IFDUP : return "OP_IFDUP";
case OP_DEPTH : return "OP_DEPTH";
case OP_DROP : return "OP_DROP";
case OP_DUP : return "OP_DUP";
case OP_NIP : return "OP_NIP";
case OP_OVER : return "OP_OVER";
case OP_PICK : return "OP_PICK";
case OP_ROLL : return "OP_ROLL";
case OP_ROT : return "OP_ROT";
case OP_SWAP : return "OP_SWAP";
case OP_TUCK : return "OP_TUCK";
// splice ops
case OP_CAT : return "OP_CAT";
case OP_SUBSTR : return "OP_SUBSTR";
case OP_LEFT : return "OP_LEFT";
case OP_RIGHT : return "OP_RIGHT";
case OP_SIZE : return "OP_SIZE";
// bit logic
case OP_INVERT : return "OP_INVERT";
case OP_AND : return "OP_AND";
case OP_OR : return "OP_OR";
case OP_XOR : return "OP_XOR";
case OP_EQUAL : return "OP_EQUAL";
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
case OP_RESERVED1 : return "OP_RESERVED1";
case OP_RESERVED2 : return "OP_RESERVED2";
// numeric
case OP_1ADD : return "OP_1ADD";
case OP_1SUB : return "OP_1SUB";
case OP_2MUL : return "OP_2MUL";
case OP_2DIV : return "OP_2DIV";
case OP_NEGATE : return "OP_NEGATE";
case OP_ABS : return "OP_ABS";
case OP_NOT : return "OP_NOT";
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
case OP_ADD : return "OP_ADD";
case OP_SUB : return "OP_SUB";
case OP_MUL : return "OP_MUL";
case OP_DIV : return "OP_DIV";
case OP_MOD : return "OP_MOD";
case OP_LSHIFT : return "OP_LSHIFT";
case OP_RSHIFT : return "OP_RSHIFT";
case OP_BOOLAND : return "OP_BOOLAND";
case OP_BOOLOR : return "OP_BOOLOR";
case OP_NUMEQUAL : return "OP_NUMEQUAL";
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
case OP_LESSTHAN : return "OP_LESSTHAN";
case OP_GREATERTHAN : return "OP_GREATERTHAN";
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
case OP_MIN : return "OP_MIN";
case OP_MAX : return "OP_MAX";
case OP_WITHIN : return "OP_WITHIN";
// crypto
case OP_RIPEMD160 : return "OP_RIPEMD160";
case OP_SHA1 : return "OP_SHA1";
case OP_SHA256 : return "OP_SHA256";
case OP_HASH160 : return "OP_HASH160";
case OP_HASH256 : return "OP_HASH256";
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
case OP_CHECKSIG : return "OP_CHECKSIG";
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
// expanson
case OP_NOP1 : return "OP_NOP1";
case OP_NOP2 : return "OP_NOP2";
case OP_NOP3 : return "OP_NOP3";
case OP_NOP4 : return "OP_NOP4";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8";
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";
// template matching params
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
return "OP_UNKNOWN";
}
};
@ -574,6 +441,7 @@ public:
return true; return true;
} }
// Encode/decode small integers:
static int DecodeOP_N(opcodetype opcode) static int DecodeOP_N(opcodetype opcode)
{ {
if (opcode == OP_0) if (opcode == OP_0)
@ -581,22 +449,44 @@ public:
assert(opcode >= OP_1 && opcode <= OP_16); assert(opcode >= OP_1 && opcode <= OP_16);
return (int)opcode - (int)(OP_1 - 1); return (int)opcode - (int)(OP_1 - 1);
} }
static opcodetype EncodeOP_N(int n)
{
assert(n >= 0 && n <= 16);
if (n == 0)
return OP_0;
return (opcodetype)(OP_1+n-1);
}
void FindAndDelete(const CScript& b) int FindAndDelete(const CScript& b)
{ {
int nFound = 0;
if (b.empty()) if (b.empty())
return; return nFound;
iterator pc = begin(); iterator pc = begin();
opcodetype opcode; opcodetype opcode;
do do
{ {
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
{
erase(pc, pc + b.size()); erase(pc, pc + b.size());
++nFound;
}
} }
while (GetOp(pc, opcode)); while (GetOp(pc, opcode));
return nFound;
}
int Find(opcodetype op) const
{
int nFound = 0;
opcodetype opcode;
for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
if (opcode == op)
++nFound;
return nFound;
} }
// This method should be removed when a compatibility-breaking block chain split has passed.
// Compatibility method for old clients that count sigops differently:
int GetSigOpCount() const int GetSigOpCount() const
{ {
int n = 0; int n = 0;
@ -614,11 +504,9 @@ public:
return n; return n;
} }
// Called by CTransaction::IsStandard
bool IsPushOnly() const bool IsPushOnly() const
{ {
if (size() > 200)
return false;
const_iterator pc = begin(); const_iterator pc = begin();
while (pc < end()) while (pc < end())
{ {
@ -632,19 +520,13 @@ public:
} }
void SetBitcoinAddress(const CBitcoinAddress& address) void SetBitcoinAddress(const CBitcoinAddress& address);
{
this->clear();
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
}
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey) void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
{ {
SetBitcoinAddress(CBitcoinAddress(vchPubKey)); SetBitcoinAddress(CBitcoinAddress(vchPubKey));
} }
void SetMultisigAnd(const std::vector<CKey>& keys); void SetMultisig(int nRequired, const std::vector<CKey>& keys);
void SetMultisigOr(const std::vector<CKey>& keys); void SetEval(const CScript& subscript);
void SetMultisigEscrow(const std::vector<CKey>& keys);
void PrintHex() const 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 Solver(const CScript& scriptPubKey, txntype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
bool IsStandard(const CScript& scriptPubKey); bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet); bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, 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 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 #endif

99
src/test/multisig_tests.cpp

@ -20,9 +20,7 @@ using namespace boost::assign;
typedef vector<unsigned char> valtype; typedef vector<unsigned char> valtype;
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, 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);
BOOST_AUTO_TEST_SUITE(multisig_tests) BOOST_AUTO_TEST_SUITE(multisig_tests)
@ -76,24 +74,25 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
vector<CKey> keys; vector<CKey> keys;
CScript s; CScript s;
int nUnused = 0;
// Test a AND b: // Test a AND b:
keys.clear(); keys.clear();
keys += key[0],key[1]; // magic operator+= from boost.assign keys += key[0],key[1]; // magic operator+= from boost.assign
s = sign_multisig(a_and_b, keys, txTo[0], 0); 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++) for (int i = 0; i < 4; i++)
{ {
keys.clear(); keys.clear();
keys += key[i]; keys += key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0); 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.clear();
keys += key[1],key[i]; keys += key[1],key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0); 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: // Test a OR b:
@ -103,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i]; keys += key[i];
s = sign_multisig(a_or_b, keys, txTo[1], 0); s = sign_multisig(a_or_b, keys, txTo[1], 0);
if (i == 0 || i == 1) 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 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.clear();
s << OP_0 << OP_0; 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.clear();
s << OP_0 << OP_1; 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++) for (int i = 0; i < 4; i++)
@ -122,16 +121,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i],key[j]; keys += key[i],key[j];
s = sign_multisig(escrow, keys, txTo[2], 0); s = sign_multisig(escrow, keys, txTo[2], 0);
if (i < j && i < 3 && j < 3) 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 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) BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{ {
CKey key[3]; CKey key[4];
for (int i = 0; i < 3; i++) for (int i = 0; i < 4; i++)
key[i].MakeNewKey(); key[i].MakeNewKey();
CScript a_and_b; CScript a_and_b;
@ -145,6 +144,21 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
CScript escrow; CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(escrow)); 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) 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; CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG; s << key[0].GetPubKey() << OP_CHECKSIG;
BOOST_CHECK(Solver(s, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1); BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr; CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr)); BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]); BOOST_CHECK(addr == keyaddr[0]);
@ -184,13 +197,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s)); BOOST_CHECK(!IsMine(emptykeystore, s));
} }
{ {
vector<vector<pair<opcodetype, valtype> > > solutions; vector<valtype> solutions;
txntype whichType;
CScript s; CScript s;
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG; 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); BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr; CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr)); BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]); BOOST_CHECK(addr == keyaddr[0]);
@ -198,47 +210,40 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s)); BOOST_CHECK(!IsMine(emptykeystore, s));
} }
{ {
vector<vector<pair<opcodetype, valtype> > > solutions; vector<valtype> solutions;
txntype whichType;
CScript s; CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1); BOOST_CHECK_EQUAL(solutions.size(), 4);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 2);
CBitcoinAddress addr; CBitcoinAddress addr;
BOOST_CHECK(!ExtractAddress(s, &keystore, addr)); BOOST_CHECK(!ExtractAddress(s, &keystore, addr));
BOOST_CHECK(IsMine(keystore, s)); BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s)); BOOST_CHECK(!IsMine(emptykeystore, s));
} }
{ {
vector<vector<pair<opcodetype, valtype> > > solutions; vector<valtype> solutions;
txntype whichType;
CScript s; CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 2); BOOST_CHECK_EQUAL(solutions.size(), 4);
if (solutions.size() == 2) vector<CBitcoinAddress> addrs;
{ int nRequired;
BOOST_CHECK(solutions[0].size() == 1); BOOST_CHECK(ExtractAddresses(s, &keystore, whichType, addrs, nRequired));
BOOST_CHECK(solutions[1].size() == 1); BOOST_CHECK(addrs[0] == keyaddr[0]);
} BOOST_CHECK(addrs[1] == keyaddr[1]);
CBitcoinAddress addr; BOOST_CHECK(nRequired = 1);
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
BOOST_CHECK(IsMine(keystore, s)); BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s)); BOOST_CHECK(!IsMine(emptykeystore, s));
} }
{ {
vector<vector<pair<opcodetype, valtype> > > solutions; vector<valtype> solutions;
txntype whichType;
CScript s; CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 3); BOOST_CHECK(solutions.size() == 5);
if (solutions.size() == 3)
{
BOOST_CHECK(solutions[0].size() == 2);
BOOST_CHECK(solutions[1].size() == 2);
BOOST_CHECK(solutions[2].size() == 2);
}
} }
} }

203
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()

40
src/test/script_tests.cpp

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

22
src/wallet.cpp

@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
return false; return false;
} }
bool CWallet::AddCScript(const uint160 &hash, const 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) bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{ {
if (!IsLocked()) if (!IsLocked())
@ -374,6 +383,16 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
return 0; 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 int64 CWalletTx::GetTxTime() const
{ {
return nTimeReceived; return nTimeReceived;
@ -443,8 +462,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
// Sent/received. Standard client will never generate a send-to-multiple-recipients, // Sent/received.
// but non-standard clients might (so return a list of address/amount pairs)
BOOST_FOREACH(const CTxOut& txout, vout) BOOST_FOREACH(const CTxOut& txout, vout)
{ {
CBitcoinAddress address; CBitcoinAddress address;

12
src/wallet.h

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

Loading…
Cancel
Save