diff --git a/src/base58.h b/src/base58.h index 113fa14a5..cba638f4e 100644 --- a/src/base58.h +++ b/src/base58.h @@ -253,14 +253,26 @@ public: }; // base58-encoded bitcoin addresses -// Addresses have version 0 or 111 (testnet) +// Public-key-hash-addresses have version 0 (or 192 testnet) // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key +// Script-hash-addresses (OP_EVAL) have version 5 (or 196 testnet) +// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script class CBitcoinAddress : public CBase58Data { public: - void SetHash160(const uint160& hash160) + enum { - SetData(fTestNet ? 111 : 0, &hash160, 20); + PUBKEY_ADDRESS = 0, + SCRIPT_ADDRESS = 5, + PUBKEY_ADDRESS_TEST = 192, + PUBKEY_ADDRESS_TEST_LEGACY = 111, // Deprecated: old testnet address + SCRIPT_ADDRESS_TEST = 196, + }; + + bool SetHash160(const uint160& hash160) + { + SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &hash160, 20); + return true; } void SetPubKey(const std::vector& vchPubKey) @@ -268,16 +280,34 @@ public: SetHash160(Hash160(vchPubKey)); } + bool SetScriptHash160(const uint160& hash160) + { + SetData(fTestNet ? SCRIPT_ADDRESS_TEST : SCRIPT_ADDRESS, &hash160, 20); + return true; + } + bool IsValid() const { int nExpectedSize = 20; bool fExpectTestNet = false; switch(nVersion) { - case 0: + case PUBKEY_ADDRESS: + nExpectedSize = 20; // Hash of public key + fExpectTestNet = false; + break; + case SCRIPT_ADDRESS: + nExpectedSize = 20; // OP_EVAL, hash of CScript + fExpectTestNet = false; break; - case 111: + case PUBKEY_ADDRESS_TEST_LEGACY: + case PUBKEY_ADDRESS_TEST: + nExpectedSize = 20; + fExpectTestNet = true; + break; + case SCRIPT_ADDRESS_TEST: + nExpectedSize = 20; fExpectTestNet = true; break; @@ -286,6 +316,14 @@ public: } return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize; } + bool IsScript() const + { + if (!IsValid()) + return false; + if (fTestNet) + return nVersion == SCRIPT_ADDRESS_TEST; + return nVersion == SCRIPT_ADDRESS; + } CBitcoinAddress() { diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index fdb1b34a0..bed90d4f6 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -673,7 +673,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) if (params.size() > 1) nMinDepth = params[1].get_int(); - // Get the set of pub keys that have the label + // Get the set of pub keys assigned to account string strAccount = AccountFromValue(params[0]); set setAddress; GetAccountAddresses(strAccount, setAddress); @@ -942,6 +942,68 @@ Value sendmany(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a bitcoin address, hex or base58 public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + if (!fTestNet) + throw runtime_error("addmultisigaddress available only when running -testnet\n"); + + int nRequired = params[0].get_int(); + const Array& keys = params[1].get_array(); + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); + + // Gather public keys + if (keys.size() < nRequired) + throw runtime_error( + strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired)); + std::vector pubkeys; + pubkeys.resize(keys.size()); + for (int i = 0; i < keys.size(); i++) + { + const std::string& ks = keys[i].get_str(); + if (ks.size() == 130) // hex public key + pubkeys[i].SetPubKey(ParseHex(ks)); + else if (ks.size() > 34) // base58-encoded + { + std::vector vchPubKey; + if (DecodeBase58(ks, vchPubKey)) + pubkeys[i].SetPubKey(vchPubKey); + else + throw runtime_error("Error base58 decoding key: "+ks); + } + else // bitcoin address for key in this wallet + { + CBitcoinAddress address(ks); + if (!pwalletMain->GetKey(address, pubkeys[i])) + throw runtime_error( + strprintf("addmultisigaddress: unknown address: %s",ks.c_str())); + } + } + + // Construct using OP_EVAL + CScript inner; + inner.SetMultisig(nRequired, pubkeys); + + uint160 scriptHash = Hash160(inner); + CScript scriptPubKey; + scriptPubKey.SetEval(inner); + pwalletMain->AddCScript(scriptHash, inner); + CBitcoinAddress address; + address.SetScriptHash160(scriptHash); + + pwalletMain->SetAddressBookName(address, strAccount); + return address.ToString(); +} + struct tallyitem { @@ -1602,7 +1664,35 @@ Value validateaddress(const Array& params, bool fHelp) // version of the address: string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0))); + if (pwalletMain->HaveKey(address)) + { + ret.push_back(Pair("ismine", true)); + std::vector vchPubKey; + pwalletMain->GetPubKey(address, vchPubKey); + ret.push_back(Pair("pubkey", HexStr(vchPubKey))); + std::string strPubKey(vchPubKey.begin(), vchPubKey.end()); + ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey))); + } + else if (pwalletMain->HaveCScript(address.GetHash160())) + { + ret.push_back(Pair("isscript", true)); + CScript subscript; + pwalletMain->GetCScript(address.GetHash160(), subscript); + ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript))); + std::vector addresses; + txnouttype whichType; + int nRequired; + ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired); + ret.push_back(Pair("script", GetTxnOutputType(whichType))); + Array a; + BOOST_FOREACH(const CBitcoinAddress& addr, addresses) + a.push_back(addr.ToString()); + ret.push_back(Pair("addresses", a)); + if (whichType == TX_MULTISIG) + ret.push_back(Pair("sigsrequired", nRequired)); + } + else + ret.push_back(Pair("ismine", false)); if (pwalletMain->mapAddressBook.count(address)) ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); } @@ -1847,6 +1937,7 @@ pair pCallTable[] = make_pair("move", &movecmd), make_pair("sendfrom", &sendfrom), make_pair("sendmany", &sendmany), + make_pair("addmultisigaddress", &addmultisigaddress), make_pair("gettransaction", &gettransaction), make_pair("listtransactions", &listtransactions), make_pair("signmessage", &signmessage), @@ -2490,6 +2581,15 @@ int CommandLineRPC(int argc, char *argv[]) params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "addmultisigaddress" && n > 1) + { + string s = params[1].get_str(); + Value v; + if (!read_string(s, v) || v.type() != array_type) + throw runtime_error("addmultisigaddress: type mismatch "+s); + params[1] = v.get_array(); + } // Execute Object reply = CallRPC(strMethod, params); diff --git a/src/crypter.h b/src/crypter.h index d7f8a39d8..e52ebc01c 100644 --- a/src/crypter.h +++ b/src/crypter.h @@ -4,6 +4,7 @@ #ifndef __CRYPTER_H__ #define __CRYPTER_H__ +#include "util.h" /* for SecureString */ #include "key.h" const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; diff --git a/src/db.cpp b/src/db.cpp index 7195e065e..f43b2a565 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -934,6 +934,15 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (nMinVersion > CLIENT_VERSION) return DB_TOO_NEW; } + else if (strType == "cscript") + { + uint160 hash; + ssKey >> hash; + CScript script; + ssValue >> script; + if (!pwallet->LoadCScript(hash, script)) + return DB_CORRUPT; + } } pcursor->close(); } diff --git a/src/db.h b/src/db.h index 15bfb29c8..e593ae28e 100644 --- a/src/db.h +++ b/src/db.h @@ -13,17 +13,17 @@ #include -class CTxIndex; +class CAccount; +class CAccountingEntry; +class CAddress; +class CBlockLocator; class CDiskBlockIndex; class CDiskTxPos; +class CMasterKey; class COutPoint; -class CAddress; -class CWalletTx; +class CTxIndex; class CWallet; -class CAccount; -class CAccountingEntry; -class CBlockLocator; - +class CWalletTx; extern unsigned int nWalletDBUpdated; extern DbEnv dbenv; @@ -420,6 +420,19 @@ public: return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + bool ReadCScript(const uint160 &hash, CScript& redeemScript) + { + redeemScript.clear(); + return Read(std::make_pair(std::string("cscript"), hash), redeemScript); + } + + bool WriteCScript(const uint160& hash, const CScript& redeemScript) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); + } + bool WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; diff --git a/src/keystore.cpp b/src/keystore.cpp index 6cf557faf..21fb0f91b 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -4,8 +4,9 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" -#include "db.h" #include "crypter.h" +#include "db.h" +#include "script.h" std::vector CKeyStore::GenerateNewKey() { @@ -33,6 +34,36 @@ bool CBasicKeyStore::AddKey(const CKey& key) return true; } +bool CBasicKeyStore::AddCScript(const uint160 &hash, const CScript& redeemScript) +{ + CRITICAL_BLOCK(cs_KeyStore) + mapScripts[hash] = redeemScript; + return true; +} + +bool CBasicKeyStore::HaveCScript(const uint160& hash) const +{ + bool result; + CRITICAL_BLOCK(cs_KeyStore) + result = (mapScripts.count(hash) > 0); + return result; +} + + +bool CBasicKeyStore::GetCScript(const uint160 &hash, CScript& redeemScriptOut) const +{ + CRITICAL_BLOCK(cs_KeyStore) + { + ScriptMap::const_iterator mi = mapScripts.find(hash); + if (mi != mapScripts.end()) + { + redeemScriptOut = (*mi).second; + return true; + } + } + return false; +} + bool CCryptoKeyStore::SetCrypted() { CRITICAL_BLOCK(cs_KeyStore) diff --git a/src/keystore.h b/src/keystore.h index 3b91de6fb..669bf9017 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -6,6 +6,7 @@ #define BITCOIN_KEYSTORE_H #include "crypter.h" +#include "script.h" // A virtual base class for key stores class CKeyStore @@ -31,6 +32,11 @@ public: virtual void GetKeys(std::set &setAddress) const =0; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector& vchPubKeyOut) const; + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript) =0; + virtual bool HaveCScript(const uint160 &hash) const =0; + virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const =0; + // Generate a new key, and add it to the store virtual std::vector GenerateNewKey(); virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const @@ -44,12 +50,14 @@ public: }; typedef std::map KeyMap; +typedef std::map ScriptMap; // Basic key store, that keeps keys in an address->secret map class CBasicKeyStore : public CKeyStore { protected: KeyMap mapKeys; + ScriptMap mapScripts; public: bool AddKey(const CKey& key); @@ -86,6 +94,9 @@ public: } return false; } + virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript); + virtual bool HaveCScript(const uint160 &hash) const; + virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const; }; typedef std::map, std::vector > > CryptedKeyMap; diff --git a/src/main.cpp b/src/main.cpp index e2f3cc9f8..cfd6ac2c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -246,6 +246,67 @@ bool CTransaction::ReadFromDisk(COutPoint prevout) return ReadFromDisk(txdb, prevout, txindex); } +bool CTransaction::IsStandard() const +{ + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // in an OP_EVAL, which is 3 ~80-byte signatures, 3 + // ~65-byte public keys, plus a few script ops. + if (txin.scriptSig.size() > 500) + return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size()); + if (!txin.scriptSig.IsPushOnly()) + return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str()); + } + BOOST_FOREACH(const CTxOut& txout, vout) + if (!::IsStandard(txout.scriptPubKey)) + return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); + return true; +} + +// +// Check transaction inputs, and make sure any +// OP_EVAL transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard DUP HASH... OP_EVAL transaction, +// which will get accepted into blocks. The script being +// EVAL'ed can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool CTransaction::AreInputsStandard(std::map > 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 > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str()); + if (whichType == TX_SCRIPTHASH) + { + vector > stack; + int nUnused; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused)) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + if (!::IsStandard(subscript)) + return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str()); + } + } + + return true; +} + int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -369,15 +430,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if ((int64)nLockTime > std::numeric_limits::max()) return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); - // Safety limits - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 34 bytes. - // 34 bytes because a TxOut is: - // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length - if (GetSigOpCount() > nSize / 34 || nSize < 100) - return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); - // Rather not work on nonstandard transactions (unless -testnet) if (!fTestNet && !IsStandard()) return error("AcceptToMemoryPool() : nonstandard transaction type"); @@ -421,15 +473,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if (fCheckInputs) { - // Check against previous transactions + map > mapInputs; map mapUnused; + if (!FetchInputs(txdb, mapUnused, false, false, mapInputs)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); + } + + // Check for non-standard OP_EVALs in inputs + if (!AreInputsStandard(mapInputs)) + return error("AcceptToMemoryPool() : nonstandard transaction input"); + + // Check against previous transactions int64 nFees = 0; - if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + int nSigOps = 0; + if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps)) { if (pfMissingInputs) *pfMissingInputs = true; return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } + // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service + // attacks disallow transactions with more than one SigOp per 65 bytes. + // 65 bytes because that is the minimum size of an ECDSA signature + unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); + if (nSigOps > nSize / 65 || nSize < 100) + return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); // Don't accept it if it can't get into a block if (nFees < GetMinFee(1000, true, true)) @@ -826,8 +897,61 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) } -bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) +bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, + bool fBlock, bool fMiner, map >& inputsRet) +{ + if (IsCoinBase()) + return true; // Coinbase transactions have no inputs to fetch. + + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + if (inputsRet.count(prevout.hash)) + continue; // Got it already + + // Read txindex + CTxIndex& txindex = inputsRet[prevout.hash].first; + bool fFound = true; + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool.find(prevout.hash)->second; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction& txPrev = inputsRet[prevout.hash].second; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (!mapTransactions.count(prevout.hash)) + return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mapTransactions[prevout.hash]; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + } + return true; +} + +bool CTransaction::ConnectInputs(map > inputs, + map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain @@ -839,43 +963,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo for (int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; - - // Read txindex - CTxIndex txindex; - bool fFound = true; - if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) - { - // Get txindex from current proposed changes - txindex = mapTestPool[prevout.hash]; - } - else - { - // Read txindex from txdb - fFound = txdb.ReadTxIndex(prevout.hash, txindex); - } - if (!fFound && (fBlock || fMiner)) - return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - - // Read txPrev - CTransaction txPrev; - if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) - { - // Get prev tx from single transactions in memory - CRITICAL_BLOCK(cs_mapTransactions) - { - if (!mapTransactions.count(prevout.hash)) - return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - txPrev = mapTransactions[prevout.hash]; - } - if (!fFound) - txindex.vSpent.resize(txPrev.vout.size()); - } - else - { - // Get prev tx from disk - if (!txPrev.ReadFromDisk(txindex.pos)) - return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - } + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); @@ -890,9 +980,27 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo // (before the last blockchain checkpoint). This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (!(fBlock && IsInitialBlockDownload())) + { + bool fStrictOpEval = true; + // This code should be removed when OP_EVAL has + // a majority of hashing power on the network. + if (fBlock) + { + // To avoid being on the short end of a block-chain split, + // interpret OP_EVAL as a NO_OP until blocks with timestamps + // after opevaltime: + int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012 + fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime); + } + // if !fBlock, then always be strict-- don't accept + // invalid-under-new-rules OP_EVAL transactions into + // our memory pool (don't relay them, don't include them + // in blocks we mine). + // Verify signature - if (!VerifySignature(txPrev, *this, i)) + if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval)) return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier @@ -965,7 +1073,8 @@ bool CTransaction::ClientConnectInputs() return false; // Verify signature - if (!VerifySignature(txPrev, *this, i)) + int nUnused = 0; + if (!VerifySignature(txPrev, *this, i, nUnused, false)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of @@ -1023,14 +1132,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) map mapQueuedChanges; int64 nFees = 0; + int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); nTxPos += ::GetSerializeSize(tx, SER_DISK); - if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false)) + map > mapInputs; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs)) + return false; + if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps)) return false; + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); } + // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@ -1291,8 +1407,21 @@ bool CBlock::CheckBlock() const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); - // Check that it's not full of nonstandard transactions - if (GetSigOpCount() > MAX_BLOCK_SIGOPS) + // This code should be removed when a compatibility-breaking block chain split has passed. + // Compatibility check for old clients that counted sigops differently: + int nSigOps = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(); + } + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(); + } + } + if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot @@ -2827,9 +2956,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) continue; - int nTxSigOps = tx.GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; // Transaction fee required depends on block size bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); @@ -2838,7 +2964,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); - if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + map > mapInputs; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs)) + continue; + int nTxSigOps = 0; + if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee)) + continue; + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; swap(mapTestPool, mapTestPoolTmp); @@ -2887,6 +3019,13 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } ++nExtraNonce; pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); + + // Put "OP_EVAL" in the coinbase so everybody can tell when + // a majority of miners support it + const char* pOpEvalName = GetOpName(OP_EVAL); + pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector(pOpEvalName, pOpEvalName+strlen(pOpEvalName)); + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } diff --git a/src/main.h b/src/main.h index 67524ef4b..4543065d7 100644 --- a/src/main.h +++ b/src/main.h @@ -493,26 +493,8 @@ public: return (vin.size() == 1 && vin[0].prevout.IsNull()); } - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTxIn& txin, vin) - n += txin.scriptSig.GetSigOpCount(); - BOOST_FOREACH(const CTxOut& txout, vout) - n += txout.scriptPubKey.GetSigOpCount(); - return n; - } - - bool IsStandard() const - { - BOOST_FOREACH(const CTxIn& txin, vin) - if (!txin.scriptSig.IsPushOnly()) - return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); - BOOST_FOREACH(const CTxOut& txout, vout) - if (!::IsStandard(txout.scriptPubKey)) - return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); - return true; - } + bool IsStandard() const; + bool AreInputsStandard(std::map > mapInputs) const; int64 GetValueOut() const { @@ -640,8 +622,13 @@ public: bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(COutPoint prevout); bool DisconnectInputs(CTxDB& txdb); - bool ConnectInputs(CTxDB& txdb, std::map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0); + + // Fetch from memory and/or disk. inputsRet keys are transaction hashes. + bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, + bool fBlock, bool fMiner, std::map >& inputsRet); + bool ConnectInputs(std::map > inputs, + std::map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0); bool ClientConnectInputs(); bool CheckTransaction() const; bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL); @@ -851,13 +838,6 @@ public: return (int64)nTime; } - int GetSigOpCount() const - { - int n = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) - n += tx.GetSigOpCount(); - return n; - } uint256 BuildMerkleTree() const diff --git a/src/makefile.osx b/src/makefile.osx index 4b0b521a3..bd72e6bc0 100644 --- a/src/makefile.osx +++ b/src/makefile.osx @@ -10,6 +10,7 @@ CXX=llvm-g++ DEPSDIR=/opt/local INCLUDEPATHS= \ + -I"$(CURDIR)" \ -I"$(DEPSDIR)/include" \ -I"$(DEPSDIR)/include/db48" @@ -112,6 +113,8 @@ obj/nogui/%.o: %.cpp bitcoind: $(OBJS:obj/%=obj/nogui/%) $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) +TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp)) + obj/test/%.o: test/%.cpp $(CXX) -c $(CFLAGS) -MMD -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ @@ -119,7 +122,7 @@ obj/test/%.o: test/%.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) -test_bitcoin: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) +test_bitcoin: $(TESTOBJS) $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) $(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(DEPSDIR)/lib/libboost_unit_test_framework-mt.a clean: diff --git a/src/makefile.unix b/src/makefile.unix index a436f968b..055f7ab29 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -6,7 +6,7 @@ USE_UPNP:=0 DEFS=-DNOPCH -DEFS += $(addprefix -I,$(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) +DEFS += $(addprefix -I,$(CURDIR) $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH)) LIBS += $(addprefix -l,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH)) LMODE = dynamic @@ -141,6 +141,8 @@ obj/nogui/%.o: %.cpp bitcoind: $(OBJS:obj/%=obj/nogui/%) $(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) +TESTOBJS := $(patsubst test/%.cpp,obj/test/%.o,$(wildcard test/*.cpp)) + obj/test/%.o: test/%.cpp $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ @@ -148,7 +150,7 @@ obj/test/%.o: test/%.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ rm -f $(@:%.o=%.d) -test_bitcoin: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) +test_bitcoin: $(TESTOBJS) $(filter-out obj/nogui/init.o,$(OBJS:obj/%=obj/nogui/%)) $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-Bstatic -lboost_unit_test_framework $(LDFLAGS) $(LIBS) clean: diff --git a/src/script.cpp b/src/script.cpp index 12d3f9e83..467cb2f0d 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -70,20 +70,186 @@ static inline void popstack(vector& stack) } -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) +const char* GetTxnOutputType(txnouttype t) +{ + switch (t) + { + case TX_NONSTANDARD: return "nonstandard"; + case TX_PUBKEY: return "pubkey"; + case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_SCRIPTHASH: return "scripthash"; + case TX_MULTISIG: return "multisig"; + } + return NULL; +} + + +const char* GetOpName(opcodetype opcode) +{ + switch (opcode) + { + // push value + case OP_0 : return "0"; + case OP_PUSHDATA1 : return "OP_PUSHDATA1"; + case OP_PUSHDATA2 : return "OP_PUSHDATA2"; + case OP_PUSHDATA4 : return "OP_PUSHDATA4"; + case OP_1NEGATE : return "-1"; + case OP_RESERVED : return "OP_RESERVED"; + case OP_1 : return "1"; + case OP_2 : return "2"; + case OP_3 : return "3"; + case OP_4 : return "4"; + case OP_5 : return "5"; + case OP_6 : return "6"; + case OP_7 : return "7"; + case OP_8 : return "8"; + case OP_9 : return "9"; + case OP_10 : return "10"; + case OP_11 : return "11"; + case OP_12 : return "12"; + case OP_13 : return "13"; + case OP_14 : return "14"; + case OP_15 : return "15"; + case OP_16 : return "16"; + + // control + case OP_NOP : return "OP_NOP"; + case OP_VER : return "OP_VER"; + case OP_IF : return "OP_IF"; + case OP_NOTIF : return "OP_NOTIF"; + case OP_VERIF : return "OP_VERIF"; + case OP_VERNOTIF : return "OP_VERNOTIF"; + case OP_ELSE : return "OP_ELSE"; + case OP_ENDIF : return "OP_ENDIF"; + case OP_VERIFY : return "OP_VERIFY"; + case OP_RETURN : return "OP_RETURN"; + + // stack ops + case OP_TOALTSTACK : return "OP_TOALTSTACK"; + case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; + case OP_2DROP : return "OP_2DROP"; + case OP_2DUP : return "OP_2DUP"; + case OP_3DUP : return "OP_3DUP"; + case OP_2OVER : return "OP_2OVER"; + case OP_2ROT : return "OP_2ROT"; + case OP_2SWAP : return "OP_2SWAP"; + case OP_IFDUP : return "OP_IFDUP"; + case OP_DEPTH : return "OP_DEPTH"; + case OP_DROP : return "OP_DROP"; + case OP_DUP : return "OP_DUP"; + case OP_NIP : return "OP_NIP"; + case OP_OVER : return "OP_OVER"; + case OP_PICK : return "OP_PICK"; + case OP_ROLL : return "OP_ROLL"; + case OP_ROT : return "OP_ROT"; + case OP_SWAP : return "OP_SWAP"; + case OP_TUCK : return "OP_TUCK"; + + // splice ops + case OP_CAT : return "OP_CAT"; + case OP_SUBSTR : return "OP_SUBSTR"; + case OP_LEFT : return "OP_LEFT"; + case OP_RIGHT : return "OP_RIGHT"; + case OP_SIZE : return "OP_SIZE"; + + // bit logic + case OP_INVERT : return "OP_INVERT"; + case OP_AND : return "OP_AND"; + case OP_OR : return "OP_OR"; + case OP_XOR : return "OP_XOR"; + case OP_EQUAL : return "OP_EQUAL"; + case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; + case OP_RESERVED1 : return "OP_RESERVED1"; + case OP_RESERVED2 : return "OP_RESERVED2"; + + // numeric + case OP_1ADD : return "OP_1ADD"; + case OP_1SUB : return "OP_1SUB"; + case OP_2MUL : return "OP_2MUL"; + case OP_2DIV : return "OP_2DIV"; + case OP_NEGATE : return "OP_NEGATE"; + case OP_ABS : return "OP_ABS"; + case OP_NOT : return "OP_NOT"; + case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; + case OP_ADD : return "OP_ADD"; + case OP_SUB : return "OP_SUB"; + case OP_MUL : return "OP_MUL"; + case OP_DIV : return "OP_DIV"; + case OP_MOD : return "OP_MOD"; + case OP_LSHIFT : return "OP_LSHIFT"; + case OP_RSHIFT : return "OP_RSHIFT"; + case OP_BOOLAND : return "OP_BOOLAND"; + case OP_BOOLOR : return "OP_BOOLOR"; + case OP_NUMEQUAL : return "OP_NUMEQUAL"; + case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; + case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; + case OP_LESSTHAN : return "OP_LESSTHAN"; + case OP_GREATERTHAN : return "OP_GREATERTHAN"; + case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; + case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; + case OP_MIN : return "OP_MIN"; + case OP_MAX : return "OP_MAX"; + case OP_WITHIN : return "OP_WITHIN"; + + // crypto + case OP_RIPEMD160 : return "OP_RIPEMD160"; + case OP_SHA1 : return "OP_SHA1"; + case OP_SHA256 : return "OP_SHA256"; + case OP_HASH160 : return "OP_HASH160"; + case OP_HASH256 : return "OP_HASH256"; + case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; + case OP_CHECKSIG : return "OP_CHECKSIG"; + case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; + case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; + case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + + // meta + case OP_EVAL : return "OP_EVAL"; + + // expanson + case OP_NOP2 : return "OP_NOP2"; + case OP_NOP3 : return "OP_NOP3"; + case OP_NOP4 : return "OP_NOP4"; + case OP_NOP5 : return "OP_NOP5"; + case OP_NOP6 : return "OP_NOP6"; + case OP_NOP7 : return "OP_NOP7"; + case OP_NOP8 : return "OP_NOP8"; + case OP_NOP9 : return "OP_NOP9"; + case OP_NOP10 : return "OP_NOP10"; + + + + // template matching params + case OP_SCRIPTHASH : return "OP_SCRIPTHASH"; + case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; + case OP_PUBKEY : return "OP_PUBKEY"; + + case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; + default: + return "OP_UNKNOWN"; + } +} + +// +// Returns true if script is valid. +// +bool EvalScriptInner(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, + CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, + bool fStrictOpEval, int nRecurseDepth) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); - CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; vector vfExec; vector altstack; if (script.size() > 10000) return false; - int nOpCount = 0; + // Limit OP_EVAL recursion + if (nRecurseDepth > 2) + return false; try { @@ -155,7 +321,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // Control // case OP_NOP: - case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: break; @@ -751,12 +917,13 @@ bool EvalScript(vector >& stack, const CScript& script, co //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); + CScript scriptCode(pbegincodehash, pendcodehash); // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); + nSigOpCount++; popstack(stack); popstack(stack); @@ -800,7 +967,7 @@ bool EvalScript(vector >& stack, const CScript& script, co return false; // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); + CScript scriptCode(pbegincodehash, pendcodehash); // Drop the signatures, since there's no way for a signature to sign itself for (int k = 0; k < nSigsCount; k++) @@ -823,6 +990,7 @@ bool EvalScript(vector >& stack, const CScript& script, co } ikey++; nKeysCount--; + nSigOpCount++; // If there are more signatures left than keys left, // then too many signatures have failed @@ -844,6 +1012,32 @@ bool EvalScript(vector >& stack, const CScript& script, co } break; + case OP_EVAL: + { + if (!fStrictOpEval) + break; // Act as a NO_OP + + + // Evaluate the top item on the stack as a Script + // [serialized script ] -- [result(s) of executing script] + if (stack.size() < 1) + return false; + valtype& vchScript = stacktop(-1); + CScript subscript(vchScript.begin(), vchScript.end()); + popstack(stack); + + // Codeseparators not allowed; they don't make sense 'inside' an OP_EVAL, because + // their purpose is to change which parts of the scriptPubKey script is copied + // and signed by OP_CHECKSIG, but OP_EVAl'ed code is in the scriptSig, not the scriptPubKey. + if (subscript.Find(OP_CODESEPARATOR)) + return false; + + if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType, + pbegincodehash, pendcodehash, nOpCount, nSigOpCount, fStrictOpEval, nRecurseDepth++)) + return false; + } + break; + default: return false; } @@ -865,6 +1059,18 @@ bool EvalScript(vector >& stack, const CScript& script, co return true; } +bool EvalScript(vector >& stack, const CScript& script, + const CTransaction& txTo, unsigned int nIn, int nHashType, + bool fStrictOpEval, int& nSigOpCountRet) +{ + CScript::const_iterator pbegincodehash = script.begin(); + CScript::const_iterator pendcodehash = script.end(); + + int nOpCount = 0; + return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash, + nOpCount, nSigOpCountRet, fStrictOpEval, 0); +} + @@ -963,25 +1169,36 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc - -bool Solver(const CScript& scriptPubKey, vector >& vSolutionRet) +// +// Return public keys or hashes from scriptPubKey, for 'standard' transaction types. +// +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) { // Templates - static vector vTemplates; - if (vTemplates.empty()) + static map mTemplates; + if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature - vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG); + mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG); + mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); + + // Sender provides N pubkeys, receivers provides M signatures + mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + + // Sender provides script hash, receiver provides script and + // as many signatures as required to satisfy script + mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL)); } // Scan templates const CScript& script1 = scriptPubKey; - BOOST_FOREACH(const CScript& script2, vTemplates) + BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) { - vSolutionRet.clear(); + const CScript& script2 = tplate.second; + vSolutionsRet.clear(); + opcodetype opcode1, opcode2; vector vch1, vch2; @@ -993,170 +1210,318 @@ bool Solver(const CScript& scriptPubKey, vector >& vSo if (pc1 == script1.end() && pc2 == script2.end()) { // Found a match - reverse(vSolutionRet.begin(), vSolutionRet.end()); + typeRet = tplate.first; + if (typeRet == TX_MULTISIG) + { + // Additional checks for TX_MULTISIG: + unsigned char m = vSolutionsRet.front()[0]; + unsigned char n = vSolutionsRet.back()[0]; + if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) + return false; + } return true; } if (!script1.GetOp(pc1, opcode1, vch1)) break; if (!script2.GetOp(pc2, opcode2, vch2)) break; + + // Template matching opcodes: + if (opcode2 == OP_PUBKEYS) + { + while (vch1.size() >= 33 && vch1.size() <= 120) + { + vSolutionsRet.push_back(vch1); + if (!script1.GetOp(pc1, opcode1, vch1)) + break; + } + if (!script2.GetOp(pc2, opcode2, vch2)) + break; + // Normal situation is to fall through + // to other if/else statments + } + if (opcode2 == OP_PUBKEY) { if (vch1.size() < 33 || vch1.size() > 120) break; - vSolutionRet.push_back(make_pair(opcode2, vch1)); + vSolutionsRet.push_back(vch1); } else if (opcode2 == OP_PUBKEYHASH) { if (vch1.size() != sizeof(uint160)) break; - vSolutionRet.push_back(make_pair(opcode2, vch1)); + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_SCRIPTHASH) + { + if (vch1.size() != sizeof(uint160)) + break; + vSolutionsRet.push_back(vch1); + } + else if (opcode2 == OP_SMALLINTEGER) + { // Single-byte small integer pushed onto vSolutions + if (opcode1 == OP_0 || + (opcode1 >= OP_1 && opcode1 <= OP_16)) + { + char n = (char)CScript::DecodeOP_N(opcode1); + vSolutionsRet.push_back(valtype(1, n)); + } + else + break; } else if (opcode1 != opcode2 || vch1 != vch2) { + // Others must match exactly break; } } } - vSolutionRet.clear(); + vSolutionsRet.clear(); + typeRet = TX_NONSTANDARD; return false; } -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) { - scriptSigRet.clear(); + CKey key; + if (!keystore.GetKey(address, key)) + return false; - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) + vector vchSig; + if (!key.Sign(hash, vchSig)) return false; + vchSig.push_back((unsigned char)nHashType); + scriptSigRet << vchSig; + + return true; +} - // Compile solution - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) +bool SignN(const vector& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + int nSigned = 0; + int nRequired = multisigdata.front()[0]; + for (vector::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++) { - if (item.first == OP_PUBKEY) - { - // Sign - const valtype& vchPubKey = item.second; - CKey key; - if (!keystore.GetKey(Hash160(vchPubKey), key)) - return false; - if (key.GetPubKey() != vchPubKey) - return false; - if (hash != 0) - { - vector vchSig; - if (!key.Sign(hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig; - } - } - else if (item.first == OP_PUBKEYHASH) + const valtype& pubkey = *it; + CBitcoinAddress address; + address.SetPubKey(pubkey); + if (Sign1(address, keystore, hash, nHashType, scriptSigRet)) { - // Sign and give pubkey - CKey key; - if (!keystore.GetKey(uint160(item.second), key)) - return false; - if (hash != 0) - { - vector vchSig; - if (!key.Sign(hash, vchSig)) - return false; - vchSig.push_back((unsigned char)nHashType); - scriptSigRet << vchSig << key.GetPubKey(); - } + ++nSigned; + if (nSigned == nRequired) break; } + } + return nSigned==nRequired; +} + +// +// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. +// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed). +// Returns true if scriptPubKey could be completely satisified. +// +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +{ + scriptSigRet.clear(); + + txnouttype whichType; + vector vSolutions; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + CBitcoinAddress address; + CScript subscript; + switch (whichType) + { + case TX_NONSTANDARD: + return false; + case TX_PUBKEY: + address.SetPubKey(vSolutions[0]); + return Sign1(address, keystore, hash, nHashType, scriptSigRet); + case TX_PUBKEYHASH: + address.SetHash160(uint160(vSolutions[0])); + if (!Sign1(address, keystore, hash, nHashType, scriptSigRet)) + return false; else { - return false; + valtype vch; + keystore.GetPubKey(address, vch); + scriptSigRet << vch; } + break; + case TX_SCRIPTHASH: + if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) + return false; + if (!Solver(keystore, subscript, hash, nHashType, scriptSigRet)) + return false; + if (hash != 0) + // static_cast to get vector.operator<< instead of CScript.operator<< + scriptSigRet << static_cast(subscript); // signatures AND serialized script + break; + case TX_MULTISIG: + scriptSigRet << OP_0; // workaround CHECKMULTISIG bug + return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); } - return true; } bool IsStandard(const CScript& scriptPubKey) { - vector > vSolution; - return Solver(scriptPubKey, vSolution); + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_MULTISIG) + { + unsigned char m = vSolutions.front()[0]; + unsigned char n = vSolutions.back()[0]; + // Support up to x-of-3 multisig txns as standard + if (n < 1 || n > 3) + return false; + if (m < 1 || m > n) + return false; + } + + return whichType != TX_NONSTANDARD; } +int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +{ + int nResult = 0; + BOOST_FOREACH(const valtype& pubkey, pubkeys) + { + CBitcoinAddress address; + address.SetPubKey(pubkey); + if (keystore.HaveKey(address)) + ++nResult; + } + return nResult; +} + bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) return false; - // Compile solution - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + CBitcoinAddress address; + switch (whichType) { - if (item.first == OP_PUBKEY) - { - const valtype& vchPubKey = item.second; - vector vchPubKeyFound; - if (!keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound)) - return false; - if (vchPubKeyFound != vchPubKey) - return false; - } - else if (item.first == OP_PUBKEYHASH) - { - if (!keystore.HaveKey(uint160(item.second))) - return false; - } - else - { + case TX_NONSTANDARD: + return false; + case TX_PUBKEY: + address.SetPubKey(vSolutions[0]); + return keystore.HaveKey(address); + case TX_PUBKEYHASH: + address.SetHash160(uint160(vSolutions[0])); + return keystore.HaveKey(address); + case TX_SCRIPTHASH: + { + CScript subscript; + if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) return false; - } + return IsMine(keystore, subscript); } - - return true; + case TX_MULTISIG: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + return HaveKeys(vSolutions, keystore); + } + } + return false; } -bool static ExtractAddressInner(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) +bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) { - vector > vSolution; - if (!Solver(scriptPubKey, vSolution)) + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) return false; - BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolution) + if (whichType == TX_PUBKEY) { - if (item.first == OP_PUBKEY) - addressRet.SetPubKey(item.second); - else if (item.first == OP_PUBKEYHASH) - addressRet.SetHash160((uint160)item.second); - if (keystore == NULL || keystore->HaveKey(addressRet)) - return true; + addressRet.SetPubKey(vSolutions[0]); + return true; } - + else if (whichType == TX_PUBKEYHASH) + { + addressRet.SetHash160(uint160(vSolutions[0])); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet.SetScriptHash160(uint160(vSolutions[0])); + return true; + } + // Multisig txns have more than one address... return false; } - -bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) +bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) { - if (keystore) - return ExtractAddressInner(scriptPubKey, keystore, addressRet); + addressRet.clear(); + typeRet = TX_NONSTANDARD; + vector vSolutions; + if (!Solver(scriptPubKey, typeRet, vSolutions)) + return false; + + if (typeRet == TX_MULTISIG) + { + nRequiredRet = vSolutions.front()[0]; + int n = vSolutions.back()[0]; + for (int i = 1; i < vSolutions.size()-1; i++) + { + CBitcoinAddress address; + address.SetPubKey(vSolutions[i]); + addressRet.push_back(address); + } + } else - return ExtractAddressInner(scriptPubKey, NULL, addressRet); - return false; -} + { + nRequiredRet = 1; + CBitcoinAddress address; + if (typeRet == TX_PUBKEYHASH) + address.SetHash160(uint160(vSolutions.front())); + else if (typeRet == TX_SCRIPTHASH) + address.SetScriptHash160(uint160(vSolutions.front())); + else if (typeRet == TX_PUBKEY) + address.SetPubKey(vSolutions.front()); + addressRet.push_back(address); + } + return true; +} -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, + int nHashType, bool fStrictOpEval) { vector > stack; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) return false; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) return false; if (stack.empty()) return false; - return CastToBool(stack.back()); + bool fResult = CastToBool(stack.back()); + + // This code should be removed when a compatibility-breaking block chain split has passed. + // Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains + // OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op: + if (fResult && fStrictOpEval && (scriptPubKey.Find(OP_EVAL) || scriptSig.Find(OP_EVAL))) + return VerifyScript(scriptSig, scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, false); + + return fResult; } @@ -1177,15 +1542,16 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans txin.scriptSig = scriptPrereq + txin.scriptSig; // Test solution + int nUnused = 0; if (scriptPrereq.empty()) - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0)) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0, true)) return false; return true; } -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType) +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType, bool fStrictOpEval) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1196,8 +1562,35 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig if (txin.prevout.hash != txFrom.GetHash()) return false; - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType)) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, fStrictOpEval)) return false; return true; } + +void CScript::SetBitcoinAddress(const CBitcoinAddress& address) +{ + this->clear(); + if (address.IsScript()) + *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL; + else + *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; +} + +void CScript::SetMultisig(int nRequired, const std::vector& keys) +{ + this->clear(); + + *this << EncodeOP_N(nRequired); + BOOST_FOREACH(const CKey& key, keys) + *this << key.GetPubKey(); + *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; +} + +void CScript::SetEval(const CScript& subscript) +{ + assert(!subscript.empty()); + uint160 subscriptHash = Hash160(subscript); + this->clear(); + *this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL; +} diff --git a/src/script.h b/src/script.h index e61ea2fd7..b671e1596 100644 --- a/src/script.h +++ b/src/script.h @@ -6,7 +6,6 @@ #define H_BITCOIN_SCRIPT #include "base58.h" -#include "keystore.h" #include #include @@ -14,6 +13,7 @@ #include class CTransaction; +class CKeyStore; enum { @@ -24,6 +24,17 @@ enum }; +enum txnouttype +{ + TX_NONSTANDARD, + // 'standard' transaction types: + TX_PUBKEY, + TX_PUBKEYHASH, + TX_SCRIPTHASH, + TX_MULTISIG, +}; + +const char* GetTxnOutputType(txnouttype t); enum opcodetype { @@ -147,8 +158,10 @@ enum opcodetype OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY, + // meta + OP_EVAL, // Was OP_NOP1 + // expansion - OP_NOP1, OP_NOP2, OP_NOP3, OP_NOP4, @@ -162,162 +175,16 @@ enum opcodetype // template matching params + OP_SMALLINTEGER = 0xfa, + OP_PUBKEYS = 0xfb, + OP_SCRIPTHASH = 0xfc, OP_PUBKEYHASH = 0xfd, OP_PUBKEY = 0xfe, OP_INVALIDOPCODE = 0xff, }; - - - - - - - -inline const char* GetOpName(opcodetype opcode) -{ - switch (opcode) - { - // push value - case OP_0 : return "0"; - case OP_PUSHDATA1 : return "OP_PUSHDATA1"; - case OP_PUSHDATA2 : return "OP_PUSHDATA2"; - case OP_PUSHDATA4 : return "OP_PUSHDATA4"; - case OP_1NEGATE : return "-1"; - case OP_RESERVED : return "OP_RESERVED"; - case OP_1 : return "1"; - case OP_2 : return "2"; - case OP_3 : return "3"; - case OP_4 : return "4"; - case OP_5 : return "5"; - case OP_6 : return "6"; - case OP_7 : return "7"; - case OP_8 : return "8"; - case OP_9 : return "9"; - case OP_10 : return "10"; - case OP_11 : return "11"; - case OP_12 : return "12"; - case OP_13 : return "13"; - case OP_14 : return "14"; - case OP_15 : return "15"; - case OP_16 : return "16"; - - // control - case OP_NOP : return "OP_NOP"; - case OP_VER : return "OP_VER"; - case OP_IF : return "OP_IF"; - case OP_NOTIF : return "OP_NOTIF"; - case OP_VERIF : return "OP_VERIF"; - case OP_VERNOTIF : return "OP_VERNOTIF"; - case OP_ELSE : return "OP_ELSE"; - case OP_ENDIF : return "OP_ENDIF"; - case OP_VERIFY : return "OP_VERIFY"; - case OP_RETURN : return "OP_RETURN"; - - // stack ops - case OP_TOALTSTACK : return "OP_TOALTSTACK"; - case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; - case OP_2DROP : return "OP_2DROP"; - case OP_2DUP : return "OP_2DUP"; - case OP_3DUP : return "OP_3DUP"; - case OP_2OVER : return "OP_2OVER"; - case OP_2ROT : return "OP_2ROT"; - case OP_2SWAP : return "OP_2SWAP"; - case OP_IFDUP : return "OP_IFDUP"; - case OP_DEPTH : return "OP_DEPTH"; - case OP_DROP : return "OP_DROP"; - case OP_DUP : return "OP_DUP"; - case OP_NIP : return "OP_NIP"; - case OP_OVER : return "OP_OVER"; - case OP_PICK : return "OP_PICK"; - case OP_ROLL : return "OP_ROLL"; - case OP_ROT : return "OP_ROT"; - case OP_SWAP : return "OP_SWAP"; - case OP_TUCK : return "OP_TUCK"; - - // splice ops - case OP_CAT : return "OP_CAT"; - case OP_SUBSTR : return "OP_SUBSTR"; - case OP_LEFT : return "OP_LEFT"; - case OP_RIGHT : return "OP_RIGHT"; - case OP_SIZE : return "OP_SIZE"; - - // bit logic - case OP_INVERT : return "OP_INVERT"; - case OP_AND : return "OP_AND"; - case OP_OR : return "OP_OR"; - case OP_XOR : return "OP_XOR"; - case OP_EQUAL : return "OP_EQUAL"; - case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; - case OP_RESERVED1 : return "OP_RESERVED1"; - case OP_RESERVED2 : return "OP_RESERVED2"; - - // numeric - case OP_1ADD : return "OP_1ADD"; - case OP_1SUB : return "OP_1SUB"; - case OP_2MUL : return "OP_2MUL"; - case OP_2DIV : return "OP_2DIV"; - case OP_NEGATE : return "OP_NEGATE"; - case OP_ABS : return "OP_ABS"; - case OP_NOT : return "OP_NOT"; - case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; - case OP_ADD : return "OP_ADD"; - case OP_SUB : return "OP_SUB"; - case OP_MUL : return "OP_MUL"; - case OP_DIV : return "OP_DIV"; - case OP_MOD : return "OP_MOD"; - case OP_LSHIFT : return "OP_LSHIFT"; - case OP_RSHIFT : return "OP_RSHIFT"; - case OP_BOOLAND : return "OP_BOOLAND"; - case OP_BOOLOR : return "OP_BOOLOR"; - case OP_NUMEQUAL : return "OP_NUMEQUAL"; - case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; - case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; - case OP_LESSTHAN : return "OP_LESSTHAN"; - case OP_GREATERTHAN : return "OP_GREATERTHAN"; - case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; - case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; - case OP_MIN : return "OP_MIN"; - case OP_MAX : return "OP_MAX"; - case OP_WITHIN : return "OP_WITHIN"; - - // crypto - case OP_RIPEMD160 : return "OP_RIPEMD160"; - case OP_SHA1 : return "OP_SHA1"; - case OP_SHA256 : return "OP_SHA256"; - case OP_HASH160 : return "OP_HASH160"; - case OP_HASH256 : return "OP_HASH256"; - case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; - case OP_CHECKSIG : return "OP_CHECKSIG"; - case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; - case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; - case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - - // expanson - case OP_NOP1 : return "OP_NOP1"; - case OP_NOP2 : return "OP_NOP2"; - case OP_NOP3 : return "OP_NOP3"; - case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; - case OP_NOP6 : return "OP_NOP6"; - case OP_NOP7 : return "OP_NOP7"; - case OP_NOP8 : return "OP_NOP8"; - case OP_NOP9 : return "OP_NOP9"; - case OP_NOP10 : return "OP_NOP10"; - - - - // template matching params - case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; - case OP_PUBKEY : return "OP_PUBKEY"; - - case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - default: - return "OP_UNKNOWN"; - } -}; - +const char* GetOpName(opcodetype opcode); @@ -574,22 +441,52 @@ public: return true; } + // Encode/decode small integers: + static int DecodeOP_N(opcodetype opcode) + { + if (opcode == OP_0) + return 0; + assert(opcode >= OP_1 && opcode <= OP_16); + return (int)opcode - (int)(OP_1 - 1); + } + static opcodetype EncodeOP_N(int n) + { + assert(n >= 0 && n <= 16); + if (n == 0) + return OP_0; + return (opcodetype)(OP_1+n-1); + } - void FindAndDelete(const CScript& b) + int FindAndDelete(const CScript& b) { + int nFound = 0; if (b.empty()) - return; + return nFound; iterator pc = begin(); opcodetype opcode; do { while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0) + { erase(pc, pc + b.size()); + ++nFound; + } } while (GetOp(pc, opcode)); + return nFound; + } + int Find(opcodetype op) const + { + int nFound = 0; + opcodetype opcode; + for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) + if (opcode == op) + ++nFound; + return nFound; } - + // This method should be removed when a compatibility-breaking block chain split has passed. + // Compatibility method for old clients that count sigops differently: int GetSigOpCount() const { int n = 0; @@ -607,11 +504,9 @@ public: return n; } - + // Called by CTransaction::IsStandard bool IsPushOnly() const { - if (size() > 200) - return false; const_iterator pc = begin(); while (pc < end()) { @@ -625,31 +520,13 @@ public: } - CBitcoinAddress GetBitcoinAddress() const - { - opcodetype opcode; - std::vector vch; - CScript::const_iterator pc = begin(); - if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0; - if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0; - if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0; - uint160 hash160 = uint160(vch); - if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0; - if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0; - if (pc != end()) return 0; - return CBitcoinAddress(hash160); - } - - void SetBitcoinAddress(const CBitcoinAddress& address) - { - this->clear(); - *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; - } - + void SetBitcoinAddress(const CBitcoinAddress& address); void SetBitcoinAddress(const std::vector& vchPubKey) { SetBitcoinAddress(CBitcoinAddress(vchPubKey)); } + void SetMultisig(int nRequired, const std::vector& keys); + void SetEval(const CScript& subscript); void PrintHex() const @@ -690,14 +567,14 @@ public: +bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, bool fStrictOpEval, int& nSigOpCountRet); - -bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType); - +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); bool IsStandard(const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet); +bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0); +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0, bool fStrictOpEval=true); #endif diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 01e669125..e9b7b4517 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -5,9 +5,10 @@ #include #include -#include "../main.h" -#include "../net.h" -#include "../util.h" +#include "main.h" +#include "wallet.h" +#include "net.h" +#include "util.h" using namespace std; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index c7fa74e96..d52ac7498 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -1,6 +1,8 @@ #include -#include "../util.h" +#include "main.h" +#include "wallet.h" +#include "util.h" BOOST_AUTO_TEST_SUITE(base58_tests) diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index f30f7f893..fff30ef5e 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -1,18 +1,20 @@ #include -#include "../util.h" +#include "main.h" +#include "wallet.h" +#include "util.h" BOOST_AUTO_TEST_SUITE(base64_tests) BOOST_AUTO_TEST_CASE(base64_testvectors) { - static const string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; - static const string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"}; + static const std::string vstrIn[] = {"","f","fo","foo","foob","fooba","foobar"}; + static const std::string vstrOut[] = {"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"}; for (int i=0; i -#include "../uint256.h" +#include "uint256.h" extern void SHA256Transform(void* pstate, void* pinput, const void* pinit); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp new file mode 100644 index 000000000..58f62b954 --- /dev/null +++ b/src/test/multisig_tests.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "keystore.h" +#include "main.h" +#include "script.h" +#include "wallet.h" + +using namespace std; +using namespace boost::assign; + +typedef vector valtype; + +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, + int nHashType, bool fStrictOpEval); + +BOOST_AUTO_TEST_SUITE(multisig_tests) + +CScript +sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, int whichIn) +{ + uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL); + + CScript result; + result << OP_0; // CHECKMULTISIG bug workaround + BOOST_FOREACH(CKey key, keys) + { + vector vchSig; + BOOST_CHECK(key.Sign(hash, vchSig)); + vchSig.push_back((unsigned char)SIGHASH_ALL); + result << vchSig; + } + return result; +} + +BOOST_AUTO_TEST_CASE(multisig_verify) +{ + CKey key[4]; + for (int i = 0; i < 4; i++) + key[i].MakeNewKey(); + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + + CTransaction txFrom; // Funding transaction + txFrom.vout.resize(3); + txFrom.vout[0].scriptPubKey = a_and_b; + txFrom.vout[1].scriptPubKey = a_or_b; + txFrom.vout[2].scriptPubKey = escrow; + + CTransaction txTo[3]; // Spending transaction + for (int i = 0; i < 3; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + } + + vector keys; + CScript s; + int nUnused = 0; + + // Test a AND b: + keys.clear(); + keys += key[0],key[1]; // magic operator+= from boost.assign + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true)); + + for (int i = 0; i < 4; i++) + { + keys.clear(); + keys += key[i]; + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 1: %d", i)); + + keys.clear(); + keys += key[1],key[i]; + s = sign_multisig(a_and_b, keys, txTo[0], 0); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 2: %d", i)); + } + + // Test a OR b: + for (int i = 0; i < 4; i++) + { + keys.clear(); + keys += key[i]; + s = sign_multisig(a_or_b, keys, txTo[1], 0); + if (i == 0 || i == 1) + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i)); + else + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i)); + } + s.clear(); + s << OP_0 << OP_0; + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true)); + s.clear(); + s << OP_0 << OP_1; + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true)); + + + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + { + keys.clear(); + keys += key[i],key[j]; + s = sign_multisig(escrow, keys, txTo[2], 0); + if (i < j && i < 3 && j < 3) + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 1: %d %d", i, j)); + else + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 2: %d %d", i, j)); + } +} + +BOOST_AUTO_TEST_CASE(multisig_IsStandard) +{ + CKey key[4]; + for (int i = 0; i < 4; i++) + key[i].MakeNewKey(); + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(a_and_b)); + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(a_or_b)); + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(::IsStandard(escrow)); + + CScript one_of_four; + one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG; + BOOST_CHECK(!::IsStandard(one_of_four)); + + CScript malformed[6]; + malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG; + malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG; + malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey(); + + for (int i = 0; i < 6; i++) + BOOST_CHECK(!::IsStandard(malformed[i])); +} + +BOOST_AUTO_TEST_CASE(multisig_Solver1) +{ + // Tests Solver() that returns lists of keys that are + // required to satisfy a ScriptPubKey + // + // Also tests IsMine() and ExtractAddress() + // + // Note: ExtractAddress for the multisignature transactions + // always returns false for this release, even if you have + // one key that would satisfy an (a|b) or 2-of-3 keys needed + // to spend an escrow transaction. + // + CBasicKeyStore keystore, emptykeystore; + CKey key[3]; + CBitcoinAddress keyaddr[3]; + for (int i = 0; i < 3; i++) + { + key[i].MakeNewKey(); + keystore.AddKey(key[i]); + keyaddr[i].SetPubKey(key[i].GetPubKey()); + } + + { + vector solutions; + txnouttype whichType; + CScript s; + s << key[0].GetPubKey() << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 1); + CBitcoinAddress addr; + BOOST_CHECK(ExtractAddress(s, &keystore, addr)); + BOOST_CHECK(addr == keyaddr[0]); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 1); + CBitcoinAddress addr; + BOOST_CHECK(ExtractAddress(s, &keystore, addr)); + BOOST_CHECK(addr == keyaddr[0]); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(solutions.size(), 4); + CBitcoinAddress addr; + BOOST_CHECK(!ExtractAddress(s, &keystore, addr)); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(solutions.size(), 4); + vector addrs; + int nRequired; + BOOST_CHECK(ExtractAddresses(s, &keystore, whichType, addrs, nRequired)); + BOOST_CHECK(addrs[0] == keyaddr[0]); + BOOST_CHECK(addrs[1] == keyaddr[1]); + BOOST_CHECK(nRequired = 1); + BOOST_CHECK(IsMine(keystore, s)); + BOOST_CHECK(!IsMine(emptykeystore, s)); + } + { + vector solutions; + txnouttype whichType; + CScript s; + s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK(solutions.size() == 5); + } +} + +BOOST_AUTO_TEST_CASE(multisig_Sign) +{ + // Test SignSignature() (and therefore the version of Solver() that signs transactions) + CBasicKeyStore keystore; + CKey key[4]; + for (int i = 0; i < 4; i++) + { + key[i].MakeNewKey(); + keystore.AddKey(key[i]); + } + + CScript a_and_b; + a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript a_or_b; + a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; + + CScript escrow; + escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; + + CTransaction txFrom; // Funding transaction + txFrom.vout.resize(3); + txFrom.vout[0].scriptPubKey = a_and_b; + txFrom.vout[1].scriptPubKey = a_or_b; + txFrom.vout[2].scriptPubKey = escrow; + + CTransaction txTo[3]; // Spending transaction + for (int i = 0; i < 3; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + } + + for (int i = 0; i < 3; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + } +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp new file mode 100644 index 000000000..c44642c6e --- /dev/null +++ b/src/test/script_op_eval_tests.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include + +#include "../main.h" +#include "../script.h" +#include "../wallet.h" + +using namespace std; + +// Test routines internal to script.cpp: +extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, + int nHashType, bool fStrictOpEval); + +BOOST_AUTO_TEST_SUITE(script_op_eval_tests) + +BOOST_AUTO_TEST_CASE(script_op_eval1) +{ + // OP_EVAL looks like this: + // scriptSig: + // scriptPubKey: DUP HASH160 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 >(recurse); + txTo.vout[0].nValue = 1; + + int nUnused = 0; + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); +} + +BOOST_AUTO_TEST_CASE(script_op_eval3) +{ + // Test the CScript::Set* methods + CBasicKeyStore keystore; + CKey key[4]; + std::vector 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(keys.begin(), keys.begin()+2)); + inner[2].SetMultisig(1, std::vector(keys.begin(), keys.begin()+2)); + inner[3].SetMultisig(2, std::vector(keys.begin(), keys.begin()+3)); + + CScript outer[4]; + for (int i = 0; i < 4; i++) + { + outer[i].SetEval(inner[i]); + keystore.AddCScript(Hash160(inner[i]), inner[i]); + } + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(4); + for (int i = 0; i < 4; i++) + { + txFrom.vout[i].scriptPubKey = outer[i]; + } + BOOST_CHECK(txFrom.IsStandard()); + + CTransaction txTo[4]; // Spending transactions + for (int i = 0; i < 4; i++) + { + txTo[i].vin.resize(1); + txTo[i].vout.resize(1); + txTo[i].vin[0].prevout.n = i; + txTo[i].vin[0].prevout.hash = txFrom.GetHash(); + txTo[i].vout[0].nValue = 1; + txTo[i].vout[0].scriptPubKey = inner[i]; + BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i)); + } + for (int i = 0; i < 4; i++) + { + BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); + BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i)); + } +} + +BOOST_AUTO_TEST_CASE(script_op_eval_backcompat1) +{ + // Check backwards-incompatibility-testing code + CScript returnsEleven; + returnsEleven << OP_11; + + // This should validate on new clients, but will + // be invalid on old clients (that interpret OP_EVAL as a no-op) + // ... except there's a special rule that makes new clients reject + // it. + CScript fund; + fund << OP_EVAL << OP_11 << OP_EQUAL; + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = fund; + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = CScript() << static_cast >(returnsEleven); + txTo.vout[0].nValue = 1; + + int nUnused = 0; + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); +} + +BOOST_AUTO_TEST_CASE(script_op_eval_switchover) +{ + // Test OP_EVAL switchover code + CScript notValid; + notValid << OP_11 << OP_12 << OP_EQUALVERIFY; + + // This will be valid under old rules, invalid under new: + CScript fund; + fund << OP_EVAL; + + CTransaction txFrom; // Funding transaction: + txFrom.vout.resize(1); + txFrom.vout[0].scriptPubKey = fund; + + CTransaction txTo; + txTo.vin.resize(1); + txTo.vout.resize(1); + txTo.vin[0].prevout.n = 0; + txTo.vin[0].prevout.hash = txFrom.GetHash(); + txTo.vin[0].scriptSig = CScript() << static_cast >(notValid); + txTo.vout[0].nValue = 1; + + int nUnused = 0; + BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, false)); + + // Under strict op_eval switchover, it should be considered invalid: + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 13feb86b9..22885a643 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2,13 +2,13 @@ #include #include -#include "../main.h" -#include "../wallet.h" +#include "main.h" +#include "wallet.h" using namespace std; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, + int nHashType, bool fStrictOpEval); BOOST_AUTO_TEST_SUITE(script_tests) @@ -21,19 +21,21 @@ BOOST_AUTO_TEST_CASE(script_PushData) static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a }; static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a }; + int nUnused = 0; + vector > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, true, nUnused)); vector > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata1Stack == directStack); vector > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata2Stack == directStack); vector > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata4Stack == directStack); } @@ -71,6 +73,7 @@ sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction) BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) { + int nUnused = 0; CKey key1, key2, key3; key1.MakeNewKey(); key2.MakeNewKey(); @@ -91,19 +94,20 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) txTo12.vout[0].nValue = 1; CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0, true)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) { + int nUnused = 0; CKey key1, key2, key3, key4; key1.MakeNewKey(); key2.MakeNewKey(); @@ -127,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, 0)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0, true)); } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 39a7c88e1..4b52b74cf 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -1,19 +1,18 @@ #define BOOST_TEST_MODULE Bitcoin Test Suite #include -#include "../main.h" -#include "../wallet.h" +#include "main.h" +#include "wallet.h" -#include "uint160_tests.cpp" -#include "uint256_tests.cpp" -#include "script_tests.cpp" -#include "transaction_tests.cpp" -#include "DoS_tests.cpp" -#include "base64_tests.cpp" -#include "util_tests.cpp" -#include "base58_tests.cpp" -#include "miner_tests.cpp" -#include "Checkpoints_tests.cpp" +extern bool fPrintToConsole; +struct TestingSetup { + TestingSetup() { + fPrintToConsole = true; // don't want to write to debug.log file + } + ~TestingSetup() { } +}; + +BOOST_GLOBAL_FIXTURE(TestingSetup); CWallet* pwalletMain; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index e6eb0f054..3268343bb 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -1,7 +1,7 @@ #include -#include "../main.h" -#include "../wallet.h" +#include "main.h" +#include "wallet.h" using namespace std; diff --git a/src/test/uint160_tests.cpp b/src/test/uint160_tests.cpp index 42c8275af..35cb35b25 100644 --- a/src/test/uint160_tests.cpp +++ b/src/test/uint160_tests.cpp @@ -1,6 +1,6 @@ #include -#include "../uint256.h" +#include "uint256.h" BOOST_AUTO_TEST_SUITE(uint160_tests) diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index c5d45e215..efdc8a6ae 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -1,6 +1,6 @@ #include -#include "../uint256.h" +#include "uint256.h" BOOST_AUTO_TEST_SUITE(uint256_tests) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 8c8b99e1b..9571c4738 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -2,7 +2,9 @@ #include #include -#include "../util.h" +#include "main.h" +#include "wallet.h" +#include "util.h" using namespace std; diff --git a/src/wallet.cpp b/src/wallet.cpp index c6f5795c0..539452841 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector return false; } +bool CWallet::AddCScript(const uint160 &hash, const CScript& redeemScript) +{ + if (!CCryptoKeyStore::AddCScript(hash, redeemScript)) + return false; + if (!fFileBacked) + return true; + return CWalletDB(strWalletFile).WriteCScript(hash, redeemScript); +} + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) @@ -374,6 +383,24 @@ int64 CWallet::GetDebit(const CTxIn &txin) const return 0; } +bool CWallet::IsChange(const CTxOut& txout) const +{ + CBitcoinAddress address; + + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a TX_PUBKEYHASH that is mine but isn't in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). + if (ExtractAddress(txout.scriptPubKey, this, address)) + CRITICAL_BLOCK(cs_wallet) + if (!mapAddressBook.count(address)) + return true; + return false; +} + int64 CWalletTx::GetTxTime() const { return nTimeReceived; @@ -443,8 +470,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l nFee = nDebit - nValueOut; } - // Sent/received. Standard client will never generate a send-to-multiple-recipients, - // but non-standard clients might (so return a list of address/amount pairs) + // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { CBitcoinAddress address; @@ -997,12 +1023,11 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW vector vchPubKey = reservekey.GetReservedKey(); // assert(mapKeys.count(vchPubKey)); - // Fill a vout to ourself, using same address type as the payment + // Fill a vout to ourself + // TODO: pass in scriptChange instead of reservekey so + // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; - if (vecSend[0].first.GetBitcoinAddress().IsValid()) - scriptChange.SetBitcoinAddress(vchPubKey); - else - scriptChange << vchPubKey << OP_CHECKSIG; + scriptChange.SetBitcoinAddress(vchPubKey); // Insert change txn at random position: vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); diff --git a/src/wallet.h b/src/wallet.h index 78f055a60..a7d07c993 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -7,6 +7,7 @@ #include "bignum.h" #include "key.h" +#include "keystore.h" #include "script.h" class CWalletTx; @@ -69,6 +70,8 @@ public: bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) bool LoadCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } + bool AddCScript(const uint160& hash, const CScript& redeemScript); + bool LoadCScript(const uint160& hash, const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(hash, redeemScript); } bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); @@ -114,15 +117,7 @@ public: throw std::runtime_error("CWallet::GetCredit() : value out of range"); return (IsMine(txout) ? txout.nValue : 0); } - bool IsChange(const CTxOut& txout) const - { - CBitcoinAddress address; - if (ExtractAddress(txout.scriptPubKey, this, address)) - CRITICAL_BLOCK(cs_wallet) - if (!mapAddressBook.count(address)) - return true; - return false; - } + bool IsChange(const CTxOut& txout) const; int64 GetChange(const CTxOut& txout) const { if (!MoneyRange(txout.nValue))