mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
OP_EVAL implementation
OP_EVAL is a new opcode that evaluates an item on the stack as a script. It enables a new type of bitcoin address that needs an arbitrarily complex script to redeem.
This commit is contained in:
parent
cc40ba2151
commit
e679ec969c
25
src/base58.h
25
src/base58.h
@ -268,6 +268,12 @@ public:
|
||||
SetHash160(Hash160(vchPubKey));
|
||||
}
|
||||
|
||||
bool SetScriptHash160(const uint160& hash160)
|
||||
{
|
||||
SetData(fTestNet ? 112 : 1, &hash160, 20);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
int nExpectedSize = 20;
|
||||
@ -275,9 +281,20 @@ public:
|
||||
switch(nVersion)
|
||||
{
|
||||
case 0:
|
||||
nExpectedSize = 20; // Hash of public key
|
||||
fExpectTestNet = false;
|
||||
break;
|
||||
case 1:
|
||||
nExpectedSize = 20; // OP_EVAL, hash of CScript
|
||||
fExpectTestNet = false;
|
||||
break;
|
||||
|
||||
case 111:
|
||||
nExpectedSize = 20;
|
||||
fExpectTestNet = true;
|
||||
break;
|
||||
case 112:
|
||||
nExpectedSize = 20;
|
||||
fExpectTestNet = true;
|
||||
break;
|
||||
|
||||
@ -286,6 +303,14 @@ public:
|
||||
}
|
||||
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
|
||||
}
|
||||
bool IsScript() const
|
||||
{
|
||||
if (!IsValid())
|
||||
return false;
|
||||
if (fTestNet)
|
||||
return nVersion == 112;
|
||||
return nVersion == 1;
|
||||
}
|
||||
|
||||
CBitcoinAddress()
|
||||
{
|
||||
|
@ -667,7 +667,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
|
||||
if (params.size() > 1)
|
||||
nMinDepth = params[1].get_int();
|
||||
|
||||
// Get the set of pub keys that have the label
|
||||
// Get the set of pub keys assigned to account
|
||||
string strAccount = AccountFromValue(params[0]);
|
||||
set<CBitcoinAddress> setAddress;
|
||||
GetAccountAddresses(strAccount, setAddress);
|
||||
@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp)
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
||||
Value sendmultisig(const Array& params, bool fHelp)
|
||||
Value addmultisigaddress(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 4 || params.size() > 7)
|
||||
if (fHelp || params.size() < 2 || params.size() > 3)
|
||||
{
|
||||
string msg = "sendmultisig <fromaccount> <type> <[\"key\",\"key\"]> <amount> [minconf=1] [comment] [comment-to]\n"
|
||||
"<type> is one of: \"and\", \"or\", \"escrow\"\n"
|
||||
"<keys> is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n"
|
||||
"<amount> is a real and is rounded to the nearest 0.00000001";
|
||||
if (pwalletMain->IsCrypted())
|
||||
msg += "\nrequires wallet passphrase to be set with walletpassphrase first";
|
||||
string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
|
||||
"Add a nrequired-to-sign multisignature address to the wallet\"\n"
|
||||
"each key is a bitcoin address, hex or base58 public key\n"
|
||||
"If [account] is specified, assign address to [account].";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
string strAccount = AccountFromValue(params[0]);
|
||||
string strType = params[1].get_str();
|
||||
const Array& keys = params[2].get_array();
|
||||
int64 nAmount = AmountFromValue(params[3]);
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 4)
|
||||
nMinDepth = params[4].get_int();
|
||||
|
||||
CWalletTx wtx;
|
||||
wtx.strFromAccount = strAccount;
|
||||
if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
|
||||
wtx.mapValue["comment"] = params[5].get_str();
|
||||
if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty())
|
||||
wtx.mapValue["to"] = params[6].get_str();
|
||||
|
||||
if (pwalletMain->IsLocked())
|
||||
throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
||||
|
||||
// Check funds
|
||||
int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
|
||||
if (nAmount > nBalance)
|
||||
throw JSONRPCError(-6, "Account has insufficient funds");
|
||||
int nRequired = params[0].get_int();
|
||||
const Array& keys = params[1].get_array();
|
||||
string strAccount;
|
||||
if (params.size() > 2)
|
||||
strAccount = AccountFromValue(params[2]);
|
||||
|
||||
// Gather public keys
|
||||
int nKeysNeeded = 0;
|
||||
if (strType == "and" || strType == "or")
|
||||
nKeysNeeded = 2;
|
||||
else if (strType == "escrow")
|
||||
nKeysNeeded = 3;
|
||||
else
|
||||
throw runtime_error("sendmultisig: <type> must be one of: and or and_or");
|
||||
if (keys.size() != nKeysNeeded)
|
||||
if (keys.size() < nRequired)
|
||||
throw runtime_error(
|
||||
strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded));
|
||||
strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
|
||||
std::vector<CKey> pubkeys;
|
||||
pubkeys.resize(nKeysNeeded);
|
||||
for (int i = 0; i < nKeysNeeded; i++)
|
||||
pubkeys.resize(keys.size());
|
||||
for (int i = 0; i < keys.size(); i++)
|
||||
{
|
||||
const std::string& ks = keys[i].get_str();
|
||||
if (ks.size() == 130) // hex public key
|
||||
@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp)
|
||||
CBitcoinAddress address(ks);
|
||||
if (!pwalletMain->GetKey(address, pubkeys[i]))
|
||||
throw runtime_error(
|
||||
strprintf("sendmultisig: unknown address: %s",ks.c_str()));
|
||||
strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Send
|
||||
// Construct using OP_EVAL
|
||||
CScript inner;
|
||||
inner.SetMultisig(nRequired, pubkeys);
|
||||
|
||||
uint160 scriptHash = Hash160(inner);
|
||||
CScript scriptPubKey;
|
||||
if (strType == "and")
|
||||
scriptPubKey.SetMultisigAnd(pubkeys);
|
||||
else if (strType == "or")
|
||||
scriptPubKey.SetMultisigOr(pubkeys);
|
||||
else
|
||||
scriptPubKey.SetMultisigEscrow(pubkeys);
|
||||
scriptPubKey.SetEval(inner);
|
||||
pwalletMain->AddCScript(scriptHash, inner);
|
||||
CBitcoinAddress address;
|
||||
address.SetScriptHash160(scriptHash);
|
||||
|
||||
CReserveKey keyChange(pwalletMain);
|
||||
int64 nFeeRequired = 0;
|
||||
bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired);
|
||||
if (!fCreated)
|
||||
{
|
||||
if (nAmount + nFeeRequired > pwalletMain->GetBalance())
|
||||
throw JSONRPCError(-6, "Insufficient funds");
|
||||
throw JSONRPCError(-4, "Transaction creation failed");
|
||||
}
|
||||
if (!pwalletMain->CommitTransaction(wtx, keyChange))
|
||||
throw JSONRPCError(-4, "Transaction commit failed");
|
||||
|
||||
return wtx.GetHash().GetHex();
|
||||
pwalletMain->SetAddressBookName(address, strAccount);
|
||||
return address.ToString();
|
||||
}
|
||||
|
||||
|
||||
@ -1700,6 +1665,24 @@ Value validateaddress(const Array& params, bool fHelp)
|
||||
std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
|
||||
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey)));
|
||||
}
|
||||
else if (pwalletMain->HaveCScript(address.GetHash160()))
|
||||
{
|
||||
ret.push_back(Pair("isscript", true));
|
||||
CScript subscript;
|
||||
pwalletMain->GetCScript(address.GetHash160(), subscript);
|
||||
ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
|
||||
std::vector<CBitcoinAddress> addresses;
|
||||
txntype whichType;
|
||||
int nRequired;
|
||||
ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
|
||||
ret.push_back(Pair("script", GetTxnTypeName(whichType)));
|
||||
Array a;
|
||||
BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
|
||||
a.push_back(addr.ToString());
|
||||
ret.push_back(Pair("addresses", a));
|
||||
if (whichType == TX_MULTISIG)
|
||||
ret.push_back(Pair("sigsrequired", nRequired));
|
||||
}
|
||||
else
|
||||
ret.push_back(Pair("ismine", false));
|
||||
if (pwalletMain->mapAddressBook.count(address))
|
||||
@ -1946,7 +1929,7 @@ pair<string, rpcfn_type> pCallTable[] =
|
||||
make_pair("move", &movecmd),
|
||||
make_pair("sendfrom", &sendfrom),
|
||||
make_pair("sendmany", &sendmany),
|
||||
make_pair("sendmultisig", &sendmultisig),
|
||||
make_pair("addmultisigaddress", &addmultisigaddress),
|
||||
make_pair("gettransaction", &gettransaction),
|
||||
make_pair("listtransactions", &listtransactions),
|
||||
make_pair("signmessage", &signmessage),
|
||||
@ -2590,16 +2573,15 @@ int CommandLineRPC(int argc, char *argv[])
|
||||
params[1] = v.get_obj();
|
||||
}
|
||||
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||
if (strMethod == "sendmultisig" && n > 2)
|
||||
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "addmultisigaddress" && n > 1)
|
||||
{
|
||||
string s = params[2].get_str();
|
||||
string s = params[1].get_str();
|
||||
Value v;
|
||||
if (!read_string(s, v) || v.type() != array_type)
|
||||
throw runtime_error("sendmultisig: type mismatch "+s);
|
||||
params[2] = v.get_array();
|
||||
throw runtime_error("addmultisigaddress: type mismatch "+s);
|
||||
params[1] = v.get_array();
|
||||
}
|
||||
if (strMethod == "sendmultisig" && n > 3) ConvertTo<double>(params[3]);
|
||||
if (strMethod == "sendmultisig" && n > 4) ConvertTo<boost::int64_t>(params[4]);
|
||||
|
||||
// Execute
|
||||
Object reply = CallRPC(strMethod, params);
|
||||
|
@ -934,6 +934,15 @@ int CWalletDB::LoadWallet(CWallet* pwallet)
|
||||
if (nMinVersion > CLIENT_VERSION)
|
||||
return DB_TOO_NEW;
|
||||
}
|
||||
else if (strType == "cscript")
|
||||
{
|
||||
uint160 hash;
|
||||
ssKey >> hash;
|
||||
std::vector<unsigned char> script;
|
||||
ssValue >> script;
|
||||
if (!pwallet->LoadCScript(hash, script))
|
||||
return DB_CORRUPT;
|
||||
}
|
||||
}
|
||||
pcursor->close();
|
||||
}
|
||||
|
12
src/db.h
12
src/db.h
@ -420,6 +420,18 @@ public:
|
||||
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
|
||||
}
|
||||
|
||||
bool ReadCScript(const uint160 &hash, std::vector<unsigned char>& data)
|
||||
{
|
||||
data.clear();
|
||||
return Read(std::make_pair(std::string("cscript"), hash), data);
|
||||
}
|
||||
|
||||
bool WriteCScript(const uint160& hash, const std::vector<unsigned char>& data)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
return Write(std::make_pair(std::string("cscript"), hash), data, false);
|
||||
}
|
||||
|
||||
bool WriteBestBlock(const CBlockLocator& locator)
|
||||
{
|
||||
nWalletDBUpdated++;
|
||||
|
@ -33,6 +33,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
|
||||
{
|
||||
CRITICAL_BLOCK(cs_KeyStore)
|
||||
mapData[hash] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBasicKeyStore::HaveCScript(const uint160& hash) const
|
||||
{
|
||||
bool result;
|
||||
CRITICAL_BLOCK(cs_KeyStore)
|
||||
result = (mapData.count(hash) > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool CBasicKeyStore::GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const
|
||||
{
|
||||
CRITICAL_BLOCK(cs_KeyStore)
|
||||
{
|
||||
DataMap::const_iterator mi = mapData.find(hash);
|
||||
if (mi != mapData.end())
|
||||
{
|
||||
dataOut = (*mi).second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCryptoKeyStore::SetCrypted()
|
||||
{
|
||||
CRITICAL_BLOCK(cs_KeyStore)
|
||||
|
@ -31,6 +31,10 @@ public:
|
||||
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
|
||||
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
|
||||
|
||||
virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data) =0;
|
||||
virtual bool HaveCScript(const uint160 &hash) const =0;
|
||||
virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const =0;
|
||||
|
||||
// Generate a new key, and add it to the store
|
||||
virtual std::vector<unsigned char> GenerateNewKey();
|
||||
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
|
||||
@ -44,12 +48,14 @@ public:
|
||||
};
|
||||
|
||||
typedef std::map<CBitcoinAddress, CSecret> KeyMap;
|
||||
typedef std::map<uint160, std::vector<unsigned char> > DataMap;
|
||||
|
||||
// Basic key store, that keeps keys in an address->secret map
|
||||
class CBasicKeyStore : public CKeyStore
|
||||
{
|
||||
protected:
|
||||
KeyMap mapKeys;
|
||||
DataMap mapData;
|
||||
|
||||
public:
|
||||
bool AddKey(const CKey& key);
|
||||
@ -86,6 +92,9 @@ public:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data);
|
||||
virtual bool HaveCScript(const uint160 &hash) const;
|
||||
virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const;
|
||||
};
|
||||
|
||||
typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;
|
||||
|
230
src/main.cpp
230
src/main.cpp
@ -246,6 +246,65 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
|
||||
return ReadFromDisk(txdb, prevout, txindex);
|
||||
}
|
||||
|
||||
bool CTransaction::IsStandard() const
|
||||
{
|
||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||
{
|
||||
// Biggest 'standard' txin is a 2-signature 2-of-3 escrow
|
||||
// in an OP_EVAL, which is 2 ~80-byte signatures, 3
|
||||
// ~65-byte public keys, plus a few script ops.
|
||||
if (txin.scriptSig.size() > 400)
|
||||
return error("nonstandard txin, size %d\n", txin.scriptSig.size());
|
||||
if (!txin.scriptSig.IsPushOnly())
|
||||
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
|
||||
}
|
||||
BOOST_FOREACH(const CTxOut& txout, vout)
|
||||
if (!::IsStandard(txout.scriptPubKey))
|
||||
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Check transaction inputs, and make sure any
|
||||
// OP_EVAL transactions are evaluating IsStandard scripts
|
||||
//
|
||||
// Why bother? To avoid denial-of-service attacks; an attacker
|
||||
// can submit a standard DUP HASH... OP_EVAL transaction,
|
||||
// which will get accepted into blocks. The script being
|
||||
// EVAL'ed can be anything; an attacker could use a very
|
||||
// expensive-to-check-upon-redemption script like:
|
||||
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
|
||||
//
|
||||
bool CTransaction::IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
|
||||
{
|
||||
if (fTestNet)
|
||||
return true; // Allow non-standard on testnet
|
||||
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
COutPoint prevout = vin[i].prevout;
|
||||
assert(mapInputs.count(prevout.hash) > 0);
|
||||
CTransaction& txPrev = mapInputs[prevout.hash].second;
|
||||
|
||||
vector<vector<unsigned char> > vSolutions;
|
||||
txntype whichType;
|
||||
if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
if (whichType == TX_SCRIPTHASH)
|
||||
{
|
||||
vector<vector<unsigned char> > stack;
|
||||
int nUnused;
|
||||
if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused))
|
||||
return false;
|
||||
const vector<unsigned char>& subscript = stack.back();
|
||||
if (!::IsStandard(CScript(subscript.begin(), subscript.end())))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
|
||||
@ -369,15 +428,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
||||
if ((int64)nLockTime > INT_MAX)
|
||||
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
|
||||
|
||||
// Safety limits
|
||||
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
||||
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
|
||||
// attacks disallow transactions with more than one SigOp per 34 bytes.
|
||||
// 34 bytes because a TxOut is:
|
||||
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
|
||||
if (GetSigOpCount() > nSize / 34 || nSize < 100)
|
||||
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
|
||||
|
||||
// Rather not work on nonstandard transactions (unless -testnet)
|
||||
if (!fTestNet && !IsStandard())
|
||||
return error("AcceptToMemoryPool() : nonstandard transaction type");
|
||||
@ -421,15 +471,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
|
||||
|
||||
if (fCheckInputs)
|
||||
{
|
||||
// Check against previous transactions
|
||||
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
|
||||
map<uint256, CTxIndex> mapUnused;
|
||||
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
|
||||
{
|
||||
if (pfMissingInputs)
|
||||
*pfMissingInputs = true;
|
||||
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
|
||||
// Check for non-standard OP_EVALs in inputs
|
||||
if (!IsStandardInputs(mapInputs))
|
||||
return error("AcceptToMemoryPool() : nonstandard transaction input");
|
||||
|
||||
// Check against previous transactions
|
||||
int64 nFees = 0;
|
||||
if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
|
||||
int nSigOps = 0;
|
||||
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
|
||||
{
|
||||
if (pfMissingInputs)
|
||||
*pfMissingInputs = true;
|
||||
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
|
||||
// attacks disallow transactions with more than one SigOp per 65 bytes.
|
||||
// 65 bytes because that is the minimum size of an ECDSA signature
|
||||
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
|
||||
if (nSigOps > nSize / 65 || nSize < 100)
|
||||
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
|
||||
|
||||
// Don't accept it if it can't get into a block
|
||||
if (nFees < GetMinFee(1000, true, true))
|
||||
@ -826,8 +895,61 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
|
||||
}
|
||||
|
||||
|
||||
bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
|
||||
bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
|
||||
bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
|
||||
{
|
||||
if (IsCoinBase())
|
||||
return true; // Coinbase transactions have no inputs to fetch.
|
||||
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
COutPoint prevout = vin[i].prevout;
|
||||
if (inputsRet.count(prevout.hash))
|
||||
continue; // Got it already
|
||||
|
||||
// Read txindex
|
||||
CTxIndex& txindex = inputsRet[prevout.hash].first;
|
||||
bool fFound = true;
|
||||
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
|
||||
{
|
||||
// Get txindex from current proposed changes
|
||||
txindex = mapTestPool.find(prevout.hash)->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read txindex from txdb
|
||||
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
|
||||
}
|
||||
if (!fFound && (fBlock || fMiner))
|
||||
return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
|
||||
// Read txPrev
|
||||
CTransaction& txPrev = inputsRet[prevout.hash].second;
|
||||
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
|
||||
{
|
||||
// Get prev tx from single transactions in memory
|
||||
CRITICAL_BLOCK(cs_mapTransactions)
|
||||
{
|
||||
if (!mapTransactions.count(prevout.hash))
|
||||
return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
txPrev = mapTransactions[prevout.hash];
|
||||
}
|
||||
if (!fFound)
|
||||
txindex.vSpent.resize(txPrev.vout.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get prev tx from disk
|
||||
if (!txPrev.ReadFromDisk(txindex.pos))
|
||||
return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
|
||||
map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
|
||||
{
|
||||
// Take over previous transactions' spent pointers
|
||||
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
|
||||
@ -839,43 +961,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
for (int i = 0; i < vin.size(); i++)
|
||||
{
|
||||
COutPoint prevout = vin[i].prevout;
|
||||
|
||||
// Read txindex
|
||||
CTxIndex txindex;
|
||||
bool fFound = true;
|
||||
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
|
||||
{
|
||||
// Get txindex from current proposed changes
|
||||
txindex = mapTestPool[prevout.hash];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read txindex from txdb
|
||||
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
|
||||
}
|
||||
if (!fFound && (fBlock || fMiner))
|
||||
return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
|
||||
// Read txPrev
|
||||
CTransaction txPrev;
|
||||
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
|
||||
{
|
||||
// Get prev tx from single transactions in memory
|
||||
CRITICAL_BLOCK(cs_mapTransactions)
|
||||
{
|
||||
if (!mapTransactions.count(prevout.hash))
|
||||
return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
txPrev = mapTransactions[prevout.hash];
|
||||
}
|
||||
if (!fFound)
|
||||
txindex.vSpent.resize(txPrev.vout.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get prev tx from disk
|
||||
if (!txPrev.ReadFromDisk(txindex.pos))
|
||||
return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
assert(inputs.count(prevout.hash) > 0);
|
||||
CTxIndex& txindex = inputs[prevout.hash].first;
|
||||
CTransaction& txPrev = inputs[prevout.hash].second;
|
||||
|
||||
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
|
||||
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
|
||||
@ -891,7 +979,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
|
||||
// still computed and checked, and any change will be caught at the next checkpoint.
|
||||
if (!(fBlock && IsInitialBlockDownload()))
|
||||
// Verify signature
|
||||
if (!VerifySignature(txPrev, *this, i))
|
||||
if (!VerifySignature(txPrev, *this, i, nSigOpsRet))
|
||||
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
|
||||
|
||||
// Check for conflicts (double-spend)
|
||||
@ -965,7 +1053,8 @@ bool CTransaction::ClientConnectInputs()
|
||||
return false;
|
||||
|
||||
// Verify signature
|
||||
if (!VerifySignature(txPrev, *this, i))
|
||||
int nUnused = 0;
|
||||
if (!VerifySignature(txPrev, *this, i, nUnused))
|
||||
return error("ConnectInputs() : VerifySignature failed");
|
||||
|
||||
///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
|
||||
@ -1023,14 +1112,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
|
||||
|
||||
map<uint256, CTxIndex> mapQueuedChanges;
|
||||
int64 nFees = 0;
|
||||
int nSigOps = 0;
|
||||
BOOST_FOREACH(CTransaction& tx, vtx)
|
||||
{
|
||||
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
|
||||
nTxPos += ::GetSerializeSize(tx, SER_DISK);
|
||||
|
||||
if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false))
|
||||
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
|
||||
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
|
||||
return false;
|
||||
if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps))
|
||||
return false;
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return DoS(100, error("ConnectBlock() : too many sigops"));
|
||||
}
|
||||
|
||||
// Write queued txindex changes
|
||||
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
|
||||
{
|
||||
@ -1291,8 +1387,21 @@ bool CBlock::CheckBlock() const
|
||||
if (!tx.CheckTransaction())
|
||||
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
|
||||
|
||||
// Check that it's not full of nonstandard transactions
|
||||
if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
|
||||
// This code should be removed when a compatibility-breaking block chain split has passed.
|
||||
// Compatibility check for old clients that counted sigops differently:
|
||||
int nSigOps = 0;
|
||||
BOOST_FOREACH(const CTransaction& tx, vtx)
|
||||
{
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
{
|
||||
nSigOps += txin.scriptSig.GetSigOpCount();
|
||||
}
|
||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||
{
|
||||
nSigOps += txout.scriptPubKey.GetSigOpCount();
|
||||
}
|
||||
}
|
||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
||||
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
|
||||
|
||||
// Check merkleroot
|
||||
@ -2827,9 +2936,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
|
||||
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
|
||||
continue;
|
||||
int nTxSigOps = tx.GetSigOpCount();
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
|
||||
// Transaction fee required depends on block size
|
||||
bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
|
||||
@ -2838,7 +2944,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
||||
// Connecting shouldn't fail due to dependency on other memory pool transactions
|
||||
// because we're already processing them in order of dependency
|
||||
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
|
||||
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
|
||||
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
|
||||
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
|
||||
continue;
|
||||
int nTxSigOps = 0;
|
||||
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
|
||||
continue;
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
swap(mapTestPool, mapTestPoolTmp);
|
||||
|
||||
|
36
src/main.h
36
src/main.h
@ -493,26 +493,8 @@ public:
|
||||
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
||||
}
|
||||
|
||||
int GetSigOpCount() const
|
||||
{
|
||||
int n = 0;
|
||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||
n += txin.scriptSig.GetSigOpCount();
|
||||
BOOST_FOREACH(const CTxOut& txout, vout)
|
||||
n += txout.scriptPubKey.GetSigOpCount();
|
||||
return n;
|
||||
}
|
||||
|
||||
bool IsStandard() const
|
||||
{
|
||||
BOOST_FOREACH(const CTxIn& txin, vin)
|
||||
if (!txin.scriptSig.IsPushOnly())
|
||||
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
|
||||
BOOST_FOREACH(const CTxOut& txout, vout)
|
||||
if (!::IsStandard(txout.scriptPubKey))
|
||||
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
|
||||
return true;
|
||||
}
|
||||
bool IsStandard() const;
|
||||
bool IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
|
||||
|
||||
int64 GetValueOut() const
|
||||
{
|
||||
@ -640,8 +622,11 @@ public:
|
||||
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
|
||||
bool ReadFromDisk(COutPoint prevout);
|
||||
bool DisconnectInputs(CTxDB& txdb);
|
||||
bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
|
||||
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
|
||||
bool fBlock, bool fMiner, std::map<uint256, std::pair<CTxIndex, CTransaction> >& inputsRet);
|
||||
bool ConnectInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > inputs,
|
||||
std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
||||
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0);
|
||||
bool ClientConnectInputs();
|
||||
bool CheckTransaction() const;
|
||||
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
||||
@ -851,13 +836,6 @@ public:
|
||||
return (int64)nTime;
|
||||
}
|
||||
|
||||
int GetSigOpCount() const
|
||||
{
|
||||
int n = 0;
|
||||
BOOST_FOREACH(const CTransaction& tx, vtx)
|
||||
n += tx.GetSigOpCount();
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
uint256 BuildMerkleTree() const
|
||||
|
674
src/script.cpp
674
src/script.cpp
@ -70,20 +70,186 @@ static inline void popstack(vector<valtype>& stack)
|
||||
}
|
||||
|
||||
|
||||
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
const char* GetTxnTypeName(txntype t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case TX_NONSTANDARD: return "nonstandard";
|
||||
case TX_PUBKEY: return "pubkey";
|
||||
case TX_PUBKEYHASH: return "pubkeyhash";
|
||||
case TX_SCRIPTHASH: return "scripthash";
|
||||
case TX_MULTISIG: return "multisig";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char* GetOpName(opcodetype opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
// push value
|
||||
case OP_0 : return "0";
|
||||
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
|
||||
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
|
||||
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
|
||||
case OP_1NEGATE : return "-1";
|
||||
case OP_RESERVED : return "OP_RESERVED";
|
||||
case OP_1 : return "1";
|
||||
case OP_2 : return "2";
|
||||
case OP_3 : return "3";
|
||||
case OP_4 : return "4";
|
||||
case OP_5 : return "5";
|
||||
case OP_6 : return "6";
|
||||
case OP_7 : return "7";
|
||||
case OP_8 : return "8";
|
||||
case OP_9 : return "9";
|
||||
case OP_10 : return "10";
|
||||
case OP_11 : return "11";
|
||||
case OP_12 : return "12";
|
||||
case OP_13 : return "13";
|
||||
case OP_14 : return "14";
|
||||
case OP_15 : return "15";
|
||||
case OP_16 : return "16";
|
||||
|
||||
// control
|
||||
case OP_NOP : return "OP_NOP";
|
||||
case OP_VER : return "OP_VER";
|
||||
case OP_IF : return "OP_IF";
|
||||
case OP_NOTIF : return "OP_NOTIF";
|
||||
case OP_VERIF : return "OP_VERIF";
|
||||
case OP_VERNOTIF : return "OP_VERNOTIF";
|
||||
case OP_ELSE : return "OP_ELSE";
|
||||
case OP_ENDIF : return "OP_ENDIF";
|
||||
case OP_VERIFY : return "OP_VERIFY";
|
||||
case OP_RETURN : return "OP_RETURN";
|
||||
|
||||
// stack ops
|
||||
case OP_TOALTSTACK : return "OP_TOALTSTACK";
|
||||
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
|
||||
case OP_2DROP : return "OP_2DROP";
|
||||
case OP_2DUP : return "OP_2DUP";
|
||||
case OP_3DUP : return "OP_3DUP";
|
||||
case OP_2OVER : return "OP_2OVER";
|
||||
case OP_2ROT : return "OP_2ROT";
|
||||
case OP_2SWAP : return "OP_2SWAP";
|
||||
case OP_IFDUP : return "OP_IFDUP";
|
||||
case OP_DEPTH : return "OP_DEPTH";
|
||||
case OP_DROP : return "OP_DROP";
|
||||
case OP_DUP : return "OP_DUP";
|
||||
case OP_NIP : return "OP_NIP";
|
||||
case OP_OVER : return "OP_OVER";
|
||||
case OP_PICK : return "OP_PICK";
|
||||
case OP_ROLL : return "OP_ROLL";
|
||||
case OP_ROT : return "OP_ROT";
|
||||
case OP_SWAP : return "OP_SWAP";
|
||||
case OP_TUCK : return "OP_TUCK";
|
||||
|
||||
// splice ops
|
||||
case OP_CAT : return "OP_CAT";
|
||||
case OP_SUBSTR : return "OP_SUBSTR";
|
||||
case OP_LEFT : return "OP_LEFT";
|
||||
case OP_RIGHT : return "OP_RIGHT";
|
||||
case OP_SIZE : return "OP_SIZE";
|
||||
|
||||
// bit logic
|
||||
case OP_INVERT : return "OP_INVERT";
|
||||
case OP_AND : return "OP_AND";
|
||||
case OP_OR : return "OP_OR";
|
||||
case OP_XOR : return "OP_XOR";
|
||||
case OP_EQUAL : return "OP_EQUAL";
|
||||
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
|
||||
case OP_RESERVED1 : return "OP_RESERVED1";
|
||||
case OP_RESERVED2 : return "OP_RESERVED2";
|
||||
|
||||
// numeric
|
||||
case OP_1ADD : return "OP_1ADD";
|
||||
case OP_1SUB : return "OP_1SUB";
|
||||
case OP_2MUL : return "OP_2MUL";
|
||||
case OP_2DIV : return "OP_2DIV";
|
||||
case OP_NEGATE : return "OP_NEGATE";
|
||||
case OP_ABS : return "OP_ABS";
|
||||
case OP_NOT : return "OP_NOT";
|
||||
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
|
||||
case OP_ADD : return "OP_ADD";
|
||||
case OP_SUB : return "OP_SUB";
|
||||
case OP_MUL : return "OP_MUL";
|
||||
case OP_DIV : return "OP_DIV";
|
||||
case OP_MOD : return "OP_MOD";
|
||||
case OP_LSHIFT : return "OP_LSHIFT";
|
||||
case OP_RSHIFT : return "OP_RSHIFT";
|
||||
case OP_BOOLAND : return "OP_BOOLAND";
|
||||
case OP_BOOLOR : return "OP_BOOLOR";
|
||||
case OP_NUMEQUAL : return "OP_NUMEQUAL";
|
||||
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
|
||||
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
|
||||
case OP_LESSTHAN : return "OP_LESSTHAN";
|
||||
case OP_GREATERTHAN : return "OP_GREATERTHAN";
|
||||
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
|
||||
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
|
||||
case OP_MIN : return "OP_MIN";
|
||||
case OP_MAX : return "OP_MAX";
|
||||
case OP_WITHIN : return "OP_WITHIN";
|
||||
|
||||
// crypto
|
||||
case OP_RIPEMD160 : return "OP_RIPEMD160";
|
||||
case OP_SHA1 : return "OP_SHA1";
|
||||
case OP_SHA256 : return "OP_SHA256";
|
||||
case OP_HASH160 : return "OP_HASH160";
|
||||
case OP_HASH256 : return "OP_HASH256";
|
||||
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
|
||||
case OP_CHECKSIG : return "OP_CHECKSIG";
|
||||
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
|
||||
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
|
||||
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
|
||||
|
||||
// meta
|
||||
case OP_EVAL : return "OP_EVAL";
|
||||
|
||||
// expanson
|
||||
case OP_NOP2 : return "OP_NOP2";
|
||||
case OP_NOP3 : return "OP_NOP3";
|
||||
case OP_NOP4 : return "OP_NOP4";
|
||||
case OP_NOP5 : return "OP_NOP5";
|
||||
case OP_NOP6 : return "OP_NOP6";
|
||||
case OP_NOP7 : return "OP_NOP7";
|
||||
case OP_NOP8 : return "OP_NOP8";
|
||||
case OP_NOP9 : return "OP_NOP9";
|
||||
case OP_NOP10 : return "OP_NOP10";
|
||||
|
||||
|
||||
|
||||
// template matching params
|
||||
case OP_SCRIPTHASH : return "OP_SCRIPTHASH";
|
||||
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
|
||||
case OP_PUBKEY : return "OP_PUBKEY";
|
||||
|
||||
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
|
||||
default:
|
||||
return "OP_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Returns true if script is valid.
|
||||
//
|
||||
bool EvalScriptInner(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
|
||||
CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, int nRecurseDepth)
|
||||
{
|
||||
CAutoBN_CTX pctx;
|
||||
CScript::const_iterator pc = script.begin();
|
||||
CScript::const_iterator pend = script.end();
|
||||
CScript::const_iterator pbegincodehash = script.begin();
|
||||
opcodetype opcode;
|
||||
valtype vchPushValue;
|
||||
vector<bool> vfExec;
|
||||
vector<valtype> altstack;
|
||||
if (script.size() > 10000)
|
||||
return false;
|
||||
int nOpCount = 0;
|
||||
|
||||
// Limit OP_EVAL recursion
|
||||
if (nRecurseDepth > 2)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
// Control
|
||||
//
|
||||
case OP_NOP:
|
||||
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
|
||||
case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
|
||||
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
|
||||
break;
|
||||
|
||||
@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");
|
||||
|
||||
// Subset of script starting at the most recent codeseparator
|
||||
CScript scriptCode(pbegincodehash, pend);
|
||||
CScript scriptCode(pbegincodehash, pendcodehash);
|
||||
|
||||
// Drop the signature, since there's no way for a signature to sign itself
|
||||
scriptCode.FindAndDelete(CScript(vchSig));
|
||||
|
||||
bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
|
||||
nSigOpCount++;
|
||||
|
||||
popstack(stack);
|
||||
popstack(stack);
|
||||
@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
return false;
|
||||
|
||||
// Subset of script starting at the most recent codeseparator
|
||||
CScript scriptCode(pbegincodehash, pend);
|
||||
CScript scriptCode(pbegincodehash, pendcodehash);
|
||||
|
||||
// Drop the signatures, since there's no way for a signature to sign itself
|
||||
for (int k = 0; k < nSigsCount; k++)
|
||||
@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
}
|
||||
ikey++;
|
||||
nKeysCount--;
|
||||
nSigOpCount++;
|
||||
|
||||
// If there are more signatures left than keys left,
|
||||
// then too many signatures have failed
|
||||
@ -844,6 +1012,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_EVAL:
|
||||
{
|
||||
// Evaluate the top item on the stack as a Script
|
||||
// [serialized script ] -- [result(s) of executing script]
|
||||
if (stack.size() < 1)
|
||||
return false;
|
||||
valtype& vchScript = stacktop(-1);
|
||||
CScript subscript(vchScript.begin(), vchScript.end());
|
||||
popstack(stack);
|
||||
|
||||
// Codeseparators not allowed
|
||||
if (subscript.Find(OP_CODESEPARATOR))
|
||||
return false;
|
||||
|
||||
if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType,
|
||||
pbegincodehash, pendcodehash, nOpCount, nSigOpCount, nRecurseDepth++))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -865,6 +1053,17 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script,
|
||||
const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet)
|
||||
{
|
||||
CScript::const_iterator pbegincodehash = script.begin();
|
||||
CScript::const_iterator pendcodehash = script.end();
|
||||
|
||||
int nOpCount = 0;
|
||||
return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash,
|
||||
nOpCount, nSigOpCountRet, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -964,38 +1163,35 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
|
||||
|
||||
|
||||
//
|
||||
// Returns lists of public keys (or public key hashes), any one of which can
|
||||
// satisfy scriptPubKey
|
||||
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
|
||||
//
|
||||
bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet)
|
||||
bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
|
||||
{
|
||||
// Templates
|
||||
static vector<CScript> vTemplates;
|
||||
if (vTemplates.empty())
|
||||
static map<txntype, CScript> mTemplates;
|
||||
if (mTemplates.empty())
|
||||
{
|
||||
// Standard tx, sender provides pubkey, receiver adds signature
|
||||
vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG);
|
||||
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
|
||||
|
||||
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
|
||||
vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG);
|
||||
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
|
||||
|
||||
// Sender provides two pubkeys, receivers provides two signatures
|
||||
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
|
||||
// Sender provides N pubkeys, receivers provides M signatures
|
||||
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
|
||||
|
||||
// Sender provides two pubkeys, receivers provides one of two signatures
|
||||
vTemplates.push_back(CScript() << OP_1 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
|
||||
|
||||
// Sender provides three pubkeys, receiver provides 2 of 3 signatures.
|
||||
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_PUBKEY << OP_3 << OP_CHECKMULTISIG);
|
||||
// Sender provides script hash, receiver provides script and
|
||||
// as many signatures as required to satisfy script
|
||||
mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL));
|
||||
}
|
||||
|
||||
// Scan templates
|
||||
const CScript& script1 = scriptPubKey;
|
||||
BOOST_FOREACH(const CScript& script2, vTemplates)
|
||||
BOOST_FOREACH(const PAIRTYPE(txntype, CScript)& tplate, mTemplates)
|
||||
{
|
||||
const CScript& script2 = tplate.second;
|
||||
vSolutionsRet.clear();
|
||||
|
||||
vector<pair<opcodetype, valtype> > currentSolution;
|
||||
opcodetype opcode1, opcode2;
|
||||
vector<unsigned char> vch1, vch2;
|
||||
|
||||
@ -1006,218 +1202,333 @@ bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype>
|
||||
{
|
||||
if (pc1 == script1.end() && pc2 == script2.end())
|
||||
{
|
||||
return !vSolutionsRet.empty();
|
||||
// Found a match
|
||||
typeRet = tplate.first;
|
||||
if (typeRet == TX_MULTISIG)
|
||||
{
|
||||
// Additional checks for TX_MULTISIG:
|
||||
unsigned char m = vSolutionsRet.front()[0];
|
||||
unsigned char n = vSolutionsRet.back()[0];
|
||||
if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!script1.GetOp(pc1, opcode1, vch1))
|
||||
break;
|
||||
if (!script2.GetOp(pc2, opcode2, vch2))
|
||||
break;
|
||||
|
||||
// Template matching opcodes:
|
||||
if (opcode2 == OP_PUBKEYS)
|
||||
{
|
||||
while (vch1.size() >= 33 && vch1.size() <= 120)
|
||||
{
|
||||
vSolutionsRet.push_back(vch1);
|
||||
if (!script1.GetOp(pc1, opcode1, vch1))
|
||||
break;
|
||||
}
|
||||
if (!script2.GetOp(pc2, opcode2, vch2))
|
||||
break;
|
||||
// Normal situation is to fall through
|
||||
// to other if/else statments
|
||||
}
|
||||
|
||||
if (opcode2 == OP_PUBKEY)
|
||||
{
|
||||
if (vch1.size() < 33 || vch1.size() > 120)
|
||||
break;
|
||||
currentSolution.push_back(make_pair(opcode2, vch1));
|
||||
vSolutionsRet.push_back(vch1);
|
||||
}
|
||||
else if (opcode2 == OP_PUBKEYHASH)
|
||||
{
|
||||
if (vch1.size() != sizeof(uint160))
|
||||
break;
|
||||
currentSolution.push_back(make_pair(opcode2, vch1));
|
||||
vSolutionsRet.push_back(vch1);
|
||||
}
|
||||
else if (opcode2 == OP_CHECKSIG)
|
||||
else if (opcode2 == OP_SCRIPTHASH)
|
||||
{
|
||||
vSolutionsRet.push_back(currentSolution);
|
||||
currentSolution.clear();
|
||||
if (vch1.size() != sizeof(uint160))
|
||||
break;
|
||||
vSolutionsRet.push_back(vch1);
|
||||
}
|
||||
else if (opcode2 == OP_CHECKMULTISIG)
|
||||
{ // Dig out the "m" from before the pubkeys:
|
||||
CScript::const_iterator it = script2.begin();
|
||||
opcodetype op_m;
|
||||
script2.GetOp(it, op_m, vch1);
|
||||
int m = CScript::DecodeOP_N(op_m);
|
||||
int n = currentSolution.size();
|
||||
|
||||
if (m == 2 && n == 2)
|
||||
else if (opcode2 == OP_SMALLINTEGER)
|
||||
{ // Single-byte small integer pushed onto vSolutions
|
||||
if (opcode1 == OP_0 ||
|
||||
(opcode1 >= OP_1 && opcode1 <= OP_16))
|
||||
{
|
||||
vSolutionsRet.push_back(currentSolution);
|
||||
currentSolution.clear();
|
||||
}
|
||||
else if (m == 1 && n == 2)
|
||||
{ // 2 solutions: either first key or second
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
vector<pair<opcodetype, valtype> > s;
|
||||
s.push_back(currentSolution[i]);
|
||||
vSolutionsRet.push_back(s);
|
||||
}
|
||||
currentSolution.clear();
|
||||
}
|
||||
else if (m == 2 && n == 3)
|
||||
{ // 3 solutions: any pair
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (int j = i+1; j < 3; j++)
|
||||
{
|
||||
vector<pair<opcodetype, valtype> > s;
|
||||
s.push_back(currentSolution[i]);
|
||||
s.push_back(currentSolution[j]);
|
||||
vSolutionsRet.push_back(s);
|
||||
}
|
||||
currentSolution.clear();
|
||||
char n = (char)CScript::DecodeOP_N(opcode1);
|
||||
vSolutionsRet.push_back(valtype(1, n));
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (opcode1 != opcode2 || vch1 != vch2)
|
||||
{
|
||||
// Others must match exactly
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vSolutionsRet.clear();
|
||||
typeRet = TX_NONSTANDARD;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
|
||||
{
|
||||
CKey key;
|
||||
if (!keystore.GetKey(address, key))
|
||||
return false;
|
||||
|
||||
vector<unsigned char> vchSig;
|
||||
if (!key.Sign(hash, vchSig))
|
||||
return false;
|
||||
vchSig.push_back((unsigned char)nHashType);
|
||||
scriptSigRet << vchSig;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
|
||||
{
|
||||
int nSigned = 0;
|
||||
int nRequired = multisigdata.front()[0];
|
||||
for (vector<valtype>::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++)
|
||||
{
|
||||
const valtype& pubkey = *it;
|
||||
CBitcoinAddress address;
|
||||
address.SetPubKey(pubkey);
|
||||
if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
|
||||
{
|
||||
++nSigned;
|
||||
if (nSigned == nRequired) break;
|
||||
}
|
||||
}
|
||||
return nSigned==nRequired;
|
||||
}
|
||||
|
||||
//
|
||||
// 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();
|
||||
|
||||
vector<vector<pair<opcodetype, valtype> > > vSolutions;
|
||||
if (!Solver(scriptPubKey, vSolutions))
|
||||
txntype whichType;
|
||||
vector<valtype> vSolutions;
|
||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
// See if we have all the keys for any of the solutions:
|
||||
int whichSolution = -1;
|
||||
for (int i = 0; i < vSolutions.size(); i++)
|
||||
CBitcoinAddress address;
|
||||
valtype subscript;
|
||||
switch (whichType)
|
||||
{
|
||||
int keysFound = 0;
|
||||
CScript scriptSig;
|
||||
|
||||
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
|
||||
{
|
||||
if (item.first == OP_PUBKEY)
|
||||
{
|
||||
const valtype& vchPubKey = item.second;
|
||||
CKey key;
|
||||
vector<unsigned char> vchSig;
|
||||
if (keystore.GetKey(Hash160(vchPubKey), key) && key.GetPubKey() == vchPubKey
|
||||
&& hash != 0 && key.Sign(hash, vchSig))
|
||||
{
|
||||
vchSig.push_back((unsigned char)nHashType);
|
||||
scriptSig << vchSig;
|
||||
++keysFound;
|
||||
}
|
||||
}
|
||||
else if (item.first == OP_PUBKEYHASH)
|
||||
{
|
||||
CKey key;
|
||||
vector<unsigned char> vchSig;
|
||||
if (keystore.GetKey(uint160(item.second), key)
|
||||
&& hash != 0 && key.Sign(hash, vchSig))
|
||||
{
|
||||
vchSig.push_back((unsigned char)nHashType);
|
||||
scriptSig << vchSig << key.GetPubKey();
|
||||
++keysFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keysFound == vSolutions[i].size())
|
||||
{
|
||||
whichSolution = i;
|
||||
scriptSigRet = scriptSig;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (whichSolution == -1)
|
||||
case TX_NONSTANDARD:
|
||||
return false;
|
||||
|
||||
// CHECKMULTISIG bug workaround:
|
||||
if (vSolutions.size() != 1 ||
|
||||
vSolutions[0].size() != 1)
|
||||
{
|
||||
scriptSigRet.insert(scriptSigRet.begin(), OP_0);
|
||||
case TX_PUBKEY:
|
||||
address.SetPubKey(vSolutions[0]);
|
||||
return Sign1(address, keystore, hash, nHashType, scriptSigRet);
|
||||
case TX_PUBKEYHASH:
|
||||
address.SetHash160(uint160(vSolutions[0]));
|
||||
if (!Sign1(address, keystore, hash, nHashType, scriptSigRet))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
valtype vch;
|
||||
keystore.GetPubKey(address, vch);
|
||||
scriptSigRet << vch;
|
||||
}
|
||||
break;
|
||||
case TX_SCRIPTHASH:
|
||||
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
|
||||
return false;
|
||||
if (!Solver(keystore, CScript(subscript.begin(), subscript.end()), hash, nHashType, scriptSigRet))
|
||||
return false;
|
||||
if (hash != 0)
|
||||
scriptSigRet << subscript; // signatures AND serialized script
|
||||
break;
|
||||
case TX_MULTISIG:
|
||||
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
|
||||
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IsStandard(const CScript& scriptPubKey)
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > vSolutions;
|
||||
return Solver(scriptPubKey, vSolutions);
|
||||
vector<valtype> vSolutions;
|
||||
txntype whichType;
|
||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
if (whichType == TX_MULTISIG)
|
||||
{
|
||||
unsigned char m = vSolutions.front()[0];
|
||||
unsigned char n = vSolutions.back()[0];
|
||||
// Support up to x-of-3 multisig txns as standard
|
||||
if (n < 1 || n > 3)
|
||||
return false;
|
||||
if (m < 1 || m > n)
|
||||
return false;
|
||||
}
|
||||
|
||||
return whichType != TX_NONSTANDARD;
|
||||
}
|
||||
|
||||
|
||||
int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
|
||||
{
|
||||
int nResult = 0;
|
||||
BOOST_FOREACH(const valtype& pubkey, pubkeys)
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
address.SetPubKey(pubkey);
|
||||
if (keystore.HaveKey(address))
|
||||
++nResult;
|
||||
}
|
||||
return nResult;
|
||||
}
|
||||
|
||||
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > vSolutions;
|
||||
if (!Solver(scriptPubKey, vSolutions))
|
||||
vector<valtype> vSolutions;
|
||||
txntype whichType;
|
||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
int keysFound = 0;
|
||||
int keysRequired = 0;
|
||||
for (int i = 0; i < vSolutions.size(); i++)
|
||||
CBitcoinAddress address;
|
||||
switch (whichType)
|
||||
{
|
||||
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
|
||||
{
|
||||
++keysRequired;
|
||||
if (item.first == OP_PUBKEY)
|
||||
{
|
||||
const valtype& vchPubKey = item.second;
|
||||
vector<unsigned char> vchPubKeyFound;
|
||||
if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey)
|
||||
++keysFound;
|
||||
}
|
||||
else if (item.first == OP_PUBKEYHASH)
|
||||
{
|
||||
if (keystore.HaveKey(uint160(item.second)))
|
||||
++keysFound;
|
||||
}
|
||||
}
|
||||
case TX_NONSTANDARD:
|
||||
return false;
|
||||
case TX_PUBKEY:
|
||||
address.SetPubKey(vSolutions[0]);
|
||||
return keystore.HaveKey(address);
|
||||
case TX_PUBKEYHASH:
|
||||
address.SetHash160(uint160(vSolutions[0]));
|
||||
return keystore.HaveKey(address);
|
||||
case TX_SCRIPTHASH:
|
||||
{
|
||||
valtype subscript;
|
||||
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
|
||||
return false;
|
||||
return IsMine(keystore, CScript(subscript.begin(), subscript.end()));
|
||||
}
|
||||
|
||||
// Only consider transactions "mine" if we own ALL the
|
||||
// keys involved. multi-signature transactions that are
|
||||
// partially owned (somebody else has a key that can spend
|
||||
// them) enable spend-out-from-under-you attacks, especially
|
||||
// for shared-wallet situations.
|
||||
return (keysFound == keysRequired);
|
||||
}
|
||||
|
||||
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > vSolutions;
|
||||
if (!Solver(scriptPubKey, vSolutions))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < vSolutions.size(); i++)
|
||||
case TX_MULTISIG:
|
||||
{
|
||||
if (vSolutions[i].size() != 1)
|
||||
continue; // Can't return more than one address...
|
||||
|
||||
PAIRTYPE(opcodetype, valtype)& item = vSolutions[i][0];
|
||||
if (item.first == OP_PUBKEY)
|
||||
addressRet.SetPubKey(item.second);
|
||||
else if (item.first == OP_PUBKEYHASH)
|
||||
addressRet.SetHash160((uint160)item.second);
|
||||
if (keystore == NULL || keystore->HaveKey(addressRet))
|
||||
return true;
|
||||
// Only consider transactions "mine" if we own ALL the
|
||||
// keys involved. multi-signature transactions that are
|
||||
// partially owned (somebody else has a key that can spend
|
||||
// them) enable spend-out-from-under-you attacks, especially
|
||||
// in shared-wallet situations.
|
||||
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
|
||||
return HaveKeys(vSolutions, keystore);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
|
||||
{
|
||||
vector<valtype> vSolutions;
|
||||
txntype whichType;
|
||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
||||
return false;
|
||||
|
||||
if (whichType == TX_PUBKEY)
|
||||
{
|
||||
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 VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txntype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
|
||||
{
|
||||
addressRet.clear();
|
||||
typeRet = TX_NONSTANDARD;
|
||||
vector<valtype> vSolutions;
|
||||
if (!Solver(scriptPubKey, typeRet, vSolutions))
|
||||
return false;
|
||||
|
||||
if (typeRet == TX_MULTISIG)
|
||||
{
|
||||
nRequiredRet = vSolutions.front()[0];
|
||||
int n = vSolutions.back()[0];
|
||||
for (vector<valtype>::const_iterator it = vSolutions.begin()+1; it != vSolutions.begin()+vSolutions.size()-1; it++)
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
address.SetPubKey(*it);
|
||||
addressRet.push_back(address);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nRequiredRet = 1;
|
||||
CBitcoinAddress address;
|
||||
if (typeRet == TX_PUBKEYHASH)
|
||||
address.SetHash160(uint160(vSolutions.front()));
|
||||
else if (typeRet == TX_SCRIPTHASH)
|
||||
address.SetScriptHash160(uint160(vSolutions.front()));
|
||||
else if (typeRet == TX_PUBKEY)
|
||||
address.SetPubKey(vSolutions.front());
|
||||
addressRet.push_back(address);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
|
||||
{
|
||||
vector<vector<unsigned char> > stack;
|
||||
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
|
||||
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet))
|
||||
return false;
|
||||
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
|
||||
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, nSigOpCountRet))
|
||||
return false;
|
||||
if (stack.empty())
|
||||
return false;
|
||||
return CastToBool(stack.back());
|
||||
bool fResult = CastToBool(stack.back());
|
||||
|
||||
// This code should be removed when a compatibility-breaking block chain split has passed.
|
||||
// Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains
|
||||
// OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op:
|
||||
if (scriptSig.Find(OP_EVAL)+scriptPubKey.Find(OP_EVAL) > 0)
|
||||
{
|
||||
int nUnused = 0;
|
||||
stack.clear();
|
||||
CScript sigCopy = scriptSig;
|
||||
sigCopy.FindAndDelete(CScript(OP_EVAL));
|
||||
CScript pubKeyCopy = scriptPubKey;
|
||||
pubKeyCopy.FindAndDelete(CScript(OP_EVAL));
|
||||
|
||||
if (!EvalScript(stack, sigCopy, txTo, nIn, nHashType, nUnused))
|
||||
return false;
|
||||
if (!EvalScript(stack, pubKeyCopy, txTo, nIn, nHashType, nUnused))
|
||||
return false;
|
||||
if (stack.empty())
|
||||
return false;
|
||||
if (fResult != CastToBool(stack.back()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return fResult;
|
||||
}
|
||||
|
||||
|
||||
@ -1238,15 +1549,16 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
|
||||
txin.scriptSig = scriptPrereq + txin.scriptSig;
|
||||
|
||||
// Test solution
|
||||
int nUnused = 0;
|
||||
if (scriptPrereq.empty())
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0))
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
|
||||
{
|
||||
assert(nIn < txTo.vin.size());
|
||||
const CTxIn& txin = txTo.vin[nIn];
|
||||
@ -1257,27 +1569,35 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
|
||||
if (txin.prevout.hash != txFrom.GetHash())
|
||||
return false;
|
||||
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
|
||||
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CScript::SetMultisigAnd(const std::vector<CKey>& keys)
|
||||
void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
|
||||
{
|
||||
assert(keys.size() >= 2);
|
||||
this->clear();
|
||||
*this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
if (address.IsScript())
|
||||
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL;
|
||||
else
|
||||
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
}
|
||||
void CScript::SetMultisigOr(const std::vector<CKey>& keys)
|
||||
|
||||
void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
|
||||
{
|
||||
assert(keys.size() >= 2);
|
||||
this->clear();
|
||||
*this << OP_1 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
|
||||
*this << EncodeOP_N(nRequired);
|
||||
BOOST_FOREACH(const CKey& key, keys)
|
||||
*this << key.GetPubKey();
|
||||
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
|
||||
}
|
||||
void CScript::SetMultisigEscrow(const std::vector<CKey>& keys)
|
||||
|
||||
void CScript::SetEval(const CScript& subscript)
|
||||
{
|
||||
assert(keys.size() >= 3);
|
||||
assert(!subscript.empty());
|
||||
uint160 subscriptHash = Hash160(subscript);
|
||||
this->clear();
|
||||
*this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << keys[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
|
||||
*this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL;
|
||||
}
|
||||
|
224
src/script.h
224
src/script.h
@ -24,6 +24,17 @@ enum
|
||||
};
|
||||
|
||||
|
||||
enum txntype
|
||||
{
|
||||
TX_NONSTANDARD,
|
||||
// 'standard' transaction types:
|
||||
TX_PUBKEY,
|
||||
TX_PUBKEYHASH,
|
||||
TX_SCRIPTHASH,
|
||||
TX_MULTISIG,
|
||||
};
|
||||
|
||||
const char* GetTxnTypeName(txntype t);
|
||||
|
||||
enum opcodetype
|
||||
{
|
||||
@ -147,8 +158,10 @@ enum opcodetype
|
||||
OP_CHECKMULTISIG,
|
||||
OP_CHECKMULTISIGVERIFY,
|
||||
|
||||
// meta
|
||||
OP_EVAL, // Was OP_NOP1
|
||||
|
||||
// expansion
|
||||
OP_NOP1,
|
||||
OP_NOP2,
|
||||
OP_NOP3,
|
||||
OP_NOP4,
|
||||
@ -162,162 +175,16 @@ enum opcodetype
|
||||
|
||||
|
||||
// template matching params
|
||||
OP_SMALLINTEGER = 0xfa,
|
||||
OP_PUBKEYS = 0xfb,
|
||||
OP_SCRIPTHASH = 0xfc,
|
||||
OP_PUBKEYHASH = 0xfd,
|
||||
OP_PUBKEY = 0xfe,
|
||||
|
||||
OP_INVALIDOPCODE = 0xff,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline const char* GetOpName(opcodetype opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
// push value
|
||||
case OP_0 : return "0";
|
||||
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
|
||||
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
|
||||
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
|
||||
case OP_1NEGATE : return "-1";
|
||||
case OP_RESERVED : return "OP_RESERVED";
|
||||
case OP_1 : return "1";
|
||||
case OP_2 : return "2";
|
||||
case OP_3 : return "3";
|
||||
case OP_4 : return "4";
|
||||
case OP_5 : return "5";
|
||||
case OP_6 : return "6";
|
||||
case OP_7 : return "7";
|
||||
case OP_8 : return "8";
|
||||
case OP_9 : return "9";
|
||||
case OP_10 : return "10";
|
||||
case OP_11 : return "11";
|
||||
case OP_12 : return "12";
|
||||
case OP_13 : return "13";
|
||||
case OP_14 : return "14";
|
||||
case OP_15 : return "15";
|
||||
case OP_16 : return "16";
|
||||
|
||||
// control
|
||||
case OP_NOP : return "OP_NOP";
|
||||
case OP_VER : return "OP_VER";
|
||||
case OP_IF : return "OP_IF";
|
||||
case OP_NOTIF : return "OP_NOTIF";
|
||||
case OP_VERIF : return "OP_VERIF";
|
||||
case OP_VERNOTIF : return "OP_VERNOTIF";
|
||||
case OP_ELSE : return "OP_ELSE";
|
||||
case OP_ENDIF : return "OP_ENDIF";
|
||||
case OP_VERIFY : return "OP_VERIFY";
|
||||
case OP_RETURN : return "OP_RETURN";
|
||||
|
||||
// stack ops
|
||||
case OP_TOALTSTACK : return "OP_TOALTSTACK";
|
||||
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
|
||||
case OP_2DROP : return "OP_2DROP";
|
||||
case OP_2DUP : return "OP_2DUP";
|
||||
case OP_3DUP : return "OP_3DUP";
|
||||
case OP_2OVER : return "OP_2OVER";
|
||||
case OP_2ROT : return "OP_2ROT";
|
||||
case OP_2SWAP : return "OP_2SWAP";
|
||||
case OP_IFDUP : return "OP_IFDUP";
|
||||
case OP_DEPTH : return "OP_DEPTH";
|
||||
case OP_DROP : return "OP_DROP";
|
||||
case OP_DUP : return "OP_DUP";
|
||||
case OP_NIP : return "OP_NIP";
|
||||
case OP_OVER : return "OP_OVER";
|
||||
case OP_PICK : return "OP_PICK";
|
||||
case OP_ROLL : return "OP_ROLL";
|
||||
case OP_ROT : return "OP_ROT";
|
||||
case OP_SWAP : return "OP_SWAP";
|
||||
case OP_TUCK : return "OP_TUCK";
|
||||
|
||||
// splice ops
|
||||
case OP_CAT : return "OP_CAT";
|
||||
case OP_SUBSTR : return "OP_SUBSTR";
|
||||
case OP_LEFT : return "OP_LEFT";
|
||||
case OP_RIGHT : return "OP_RIGHT";
|
||||
case OP_SIZE : return "OP_SIZE";
|
||||
|
||||
// bit logic
|
||||
case OP_INVERT : return "OP_INVERT";
|
||||
case OP_AND : return "OP_AND";
|
||||
case OP_OR : return "OP_OR";
|
||||
case OP_XOR : return "OP_XOR";
|
||||
case OP_EQUAL : return "OP_EQUAL";
|
||||
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
|
||||
case OP_RESERVED1 : return "OP_RESERVED1";
|
||||
case OP_RESERVED2 : return "OP_RESERVED2";
|
||||
|
||||
// numeric
|
||||
case OP_1ADD : return "OP_1ADD";
|
||||
case OP_1SUB : return "OP_1SUB";
|
||||
case OP_2MUL : return "OP_2MUL";
|
||||
case OP_2DIV : return "OP_2DIV";
|
||||
case OP_NEGATE : return "OP_NEGATE";
|
||||
case OP_ABS : return "OP_ABS";
|
||||
case OP_NOT : return "OP_NOT";
|
||||
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
|
||||
case OP_ADD : return "OP_ADD";
|
||||
case OP_SUB : return "OP_SUB";
|
||||
case OP_MUL : return "OP_MUL";
|
||||
case OP_DIV : return "OP_DIV";
|
||||
case OP_MOD : return "OP_MOD";
|
||||
case OP_LSHIFT : return "OP_LSHIFT";
|
||||
case OP_RSHIFT : return "OP_RSHIFT";
|
||||
case OP_BOOLAND : return "OP_BOOLAND";
|
||||
case OP_BOOLOR : return "OP_BOOLOR";
|
||||
case OP_NUMEQUAL : return "OP_NUMEQUAL";
|
||||
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
|
||||
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
|
||||
case OP_LESSTHAN : return "OP_LESSTHAN";
|
||||
case OP_GREATERTHAN : return "OP_GREATERTHAN";
|
||||
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
|
||||
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
|
||||
case OP_MIN : return "OP_MIN";
|
||||
case OP_MAX : return "OP_MAX";
|
||||
case OP_WITHIN : return "OP_WITHIN";
|
||||
|
||||
// crypto
|
||||
case OP_RIPEMD160 : return "OP_RIPEMD160";
|
||||
case OP_SHA1 : return "OP_SHA1";
|
||||
case OP_SHA256 : return "OP_SHA256";
|
||||
case OP_HASH160 : return "OP_HASH160";
|
||||
case OP_HASH256 : return "OP_HASH256";
|
||||
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
|
||||
case OP_CHECKSIG : return "OP_CHECKSIG";
|
||||
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
|
||||
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
|
||||
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
|
||||
|
||||
// expanson
|
||||
case OP_NOP1 : return "OP_NOP1";
|
||||
case OP_NOP2 : return "OP_NOP2";
|
||||
case OP_NOP3 : return "OP_NOP3";
|
||||
case OP_NOP4 : return "OP_NOP4";
|
||||
case OP_NOP5 : return "OP_NOP5";
|
||||
case OP_NOP6 : return "OP_NOP6";
|
||||
case OP_NOP7 : return "OP_NOP7";
|
||||
case OP_NOP8 : return "OP_NOP8";
|
||||
case OP_NOP9 : return "OP_NOP9";
|
||||
case OP_NOP10 : return "OP_NOP10";
|
||||
|
||||
|
||||
|
||||
// template matching params
|
||||
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
|
||||
case OP_PUBKEY : return "OP_PUBKEY";
|
||||
|
||||
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
|
||||
default:
|
||||
return "OP_UNKNOWN";
|
||||
}
|
||||
};
|
||||
|
||||
const char* GetOpName(opcodetype opcode);
|
||||
|
||||
|
||||
|
||||
@ -574,6 +441,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode/decode small integers:
|
||||
static int DecodeOP_N(opcodetype opcode)
|
||||
{
|
||||
if (opcode == OP_0)
|
||||
@ -581,22 +449,44 @@ public:
|
||||
assert(opcode >= OP_1 && opcode <= OP_16);
|
||||
return (int)opcode - (int)(OP_1 - 1);
|
||||
}
|
||||
|
||||
void FindAndDelete(const CScript& b)
|
||||
static opcodetype EncodeOP_N(int n)
|
||||
{
|
||||
assert(n >= 0 && n <= 16);
|
||||
if (n == 0)
|
||||
return OP_0;
|
||||
return (opcodetype)(OP_1+n-1);
|
||||
}
|
||||
|
||||
int FindAndDelete(const CScript& b)
|
||||
{
|
||||
int nFound = 0;
|
||||
if (b.empty())
|
||||
return;
|
||||
return nFound;
|
||||
iterator pc = begin();
|
||||
opcodetype opcode;
|
||||
do
|
||||
{
|
||||
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
|
||||
{
|
||||
erase(pc, pc + b.size());
|
||||
++nFound;
|
||||
}
|
||||
}
|
||||
while (GetOp(pc, opcode));
|
||||
return nFound;
|
||||
}
|
||||
int Find(opcodetype op) const
|
||||
{
|
||||
int nFound = 0;
|
||||
opcodetype opcode;
|
||||
for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
|
||||
if (opcode == op)
|
||||
++nFound;
|
||||
return nFound;
|
||||
}
|
||||
|
||||
|
||||
// This method should be removed when a compatibility-breaking block chain split has passed.
|
||||
// Compatibility method for old clients that count sigops differently:
|
||||
int GetSigOpCount() const
|
||||
{
|
||||
int n = 0;
|
||||
@ -614,11 +504,9 @@ public:
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
// Called by CTransaction::IsStandard
|
||||
bool IsPushOnly() const
|
||||
{
|
||||
if (size() > 200)
|
||||
return false;
|
||||
const_iterator pc = begin();
|
||||
while (pc < end())
|
||||
{
|
||||
@ -632,19 +520,13 @@ public:
|
||||
}
|
||||
|
||||
|
||||
void SetBitcoinAddress(const CBitcoinAddress& address)
|
||||
{
|
||||
this->clear();
|
||||
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
}
|
||||
|
||||
void SetBitcoinAddress(const CBitcoinAddress& address);
|
||||
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
|
||||
{
|
||||
SetBitcoinAddress(CBitcoinAddress(vchPubKey));
|
||||
}
|
||||
void SetMultisigAnd(const std::vector<CKey>& keys);
|
||||
void SetMultisigOr(const std::vector<CKey>& keys);
|
||||
void SetMultisigEscrow(const std::vector<CKey>& keys);
|
||||
void SetMultisig(int nRequired, const std::vector<CKey>& keys);
|
||||
void SetEval(const CScript& subscript);
|
||||
|
||||
|
||||
void PrintHex() const
|
||||
@ -685,14 +567,14 @@ public:
|
||||
|
||||
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet);
|
||||
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
||||
bool Solver(const CScript& scriptPubKey, txntype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
|
||||
bool IsStandard(const CScript& scriptPubKey);
|
||||
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
||||
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
|
||||
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txntype& typeRet, std::vector<CBitcoinAddress>& addressRet, int& nRequiredRet);
|
||||
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0);
|
||||
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0);
|
||||
|
||||
#endif
|
||||
|
@ -20,9 +20,7 @@ using namespace boost::assign;
|
||||
typedef vector<unsigned char> valtype;
|
||||
|
||||
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet);
|
||||
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, int nHashType);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(multisig_tests)
|
||||
|
||||
@ -76,24 +74,25 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
|
||||
|
||||
vector<CKey> keys;
|
||||
CScript s;
|
||||
int nUnused = 0;
|
||||
|
||||
// Test a AND b:
|
||||
keys.clear();
|
||||
keys += key[0],key[1]; // magic operator+= from boost.assign
|
||||
s = sign_multisig(a_and_b, keys, txTo[0], 0);
|
||||
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, 0));
|
||||
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0));
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
keys.clear();
|
||||
keys += key[i];
|
||||
s = sign_multisig(a_and_b, keys, txTo[0], 0);
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 1: %d", i));
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 1: %d", i));
|
||||
|
||||
keys.clear();
|
||||
keys += key[1],key[i];
|
||||
s = sign_multisig(a_and_b, keys, txTo[0], 0);
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 2: %d", i));
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 2: %d", i));
|
||||
}
|
||||
|
||||
// Test a OR b:
|
||||
@ -103,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
|
||||
keys += key[i];
|
||||
s = sign_multisig(a_or_b, keys, txTo[1], 0);
|
||||
if (i == 0 || i == 1)
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
|
||||
else
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
|
||||
}
|
||||
s.clear();
|
||||
s << OP_0 << OP_0;
|
||||
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
|
||||
s.clear();
|
||||
s << OP_0 << OP_1;
|
||||
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
|
||||
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
@ -122,16 +121,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
|
||||
keys += key[i],key[j];
|
||||
s = sign_multisig(escrow, keys, txTo[2], 0);
|
||||
if (i < j && i < 3 && j < 3)
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 1: %d %d", i, j));
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 1: %d %d", i, j));
|
||||
else
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 2: %d %d", i, j));
|
||||
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 2: %d %d", i, j));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multisig_IsStandard)
|
||||
{
|
||||
CKey key[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
CKey key[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
key[i].MakeNewKey();
|
||||
|
||||
CScript a_and_b;
|
||||
@ -145,6 +144,21 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
|
||||
CScript escrow;
|
||||
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
|
||||
BOOST_CHECK(::IsStandard(escrow));
|
||||
|
||||
CScript one_of_four;
|
||||
one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG;
|
||||
BOOST_CHECK(!::IsStandard(one_of_four));
|
||||
|
||||
CScript malformed[6];
|
||||
malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
|
||||
malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG;
|
||||
malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG;
|
||||
malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey();
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
BOOST_CHECK(!::IsStandard(malformed[i]));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multisig_Solver1)
|
||||
@ -170,13 +184,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
|
||||
}
|
||||
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > solutions;
|
||||
vector<valtype> solutions;
|
||||
txntype whichType;
|
||||
CScript s;
|
||||
s << key[0].GetPubKey() << OP_CHECKSIG;
|
||||
BOOST_CHECK(Solver(s, solutions));
|
||||
BOOST_CHECK(Solver(s, whichType, solutions));
|
||||
BOOST_CHECK(solutions.size() == 1);
|
||||
if (solutions.size() == 1)
|
||||
BOOST_CHECK(solutions[0].size() == 1);
|
||||
CBitcoinAddress addr;
|
||||
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
|
||||
BOOST_CHECK(addr == keyaddr[0]);
|
||||
@ -184,13 +197,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
|
||||
BOOST_CHECK(!IsMine(emptykeystore, s));
|
||||
}
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > solutions;
|
||||
vector<valtype> solutions;
|
||||
txntype whichType;
|
||||
CScript s;
|
||||
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||
BOOST_CHECK(Solver(s, solutions));
|
||||
BOOST_CHECK(Solver(s, whichType, solutions));
|
||||
BOOST_CHECK(solutions.size() == 1);
|
||||
if (solutions.size() == 1)
|
||||
BOOST_CHECK(solutions[0].size() == 1);
|
||||
CBitcoinAddress addr;
|
||||
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
|
||||
BOOST_CHECK(addr == keyaddr[0]);
|
||||
@ -198,47 +210,40 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
|
||||
BOOST_CHECK(!IsMine(emptykeystore, s));
|
||||
}
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > solutions;
|
||||
vector<valtype> solutions;
|
||||
txntype whichType;
|
||||
CScript s;
|
||||
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
BOOST_CHECK(Solver(s, solutions));
|
||||
BOOST_CHECK(solutions.size() == 1);
|
||||
if (solutions.size() == 1)
|
||||
BOOST_CHECK(solutions[0].size() == 2);
|
||||
BOOST_CHECK(Solver(s, whichType, solutions));
|
||||
BOOST_CHECK_EQUAL(solutions.size(), 4);
|
||||
CBitcoinAddress addr;
|
||||
BOOST_CHECK(!ExtractAddress(s, &keystore, addr));
|
||||
BOOST_CHECK(IsMine(keystore, s));
|
||||
BOOST_CHECK(!IsMine(emptykeystore, s));
|
||||
}
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > solutions;
|
||||
vector<valtype> solutions;
|
||||
txntype whichType;
|
||||
CScript s;
|
||||
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
|
||||
BOOST_CHECK(Solver(s, solutions));
|
||||
BOOST_CHECK(solutions.size() == 2);
|
||||
if (solutions.size() == 2)
|
||||
{
|
||||
BOOST_CHECK(solutions[0].size() == 1);
|
||||
BOOST_CHECK(solutions[1].size() == 1);
|
||||
}
|
||||
CBitcoinAddress addr;
|
||||
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
|
||||
BOOST_CHECK(addr == keyaddr[0]);
|
||||
BOOST_CHECK(Solver(s, whichType, solutions));
|
||||
BOOST_CHECK_EQUAL(solutions.size(), 4);
|
||||
vector<CBitcoinAddress> addrs;
|
||||
int nRequired;
|
||||
BOOST_CHECK(ExtractAddresses(s, &keystore, whichType, addrs, nRequired));
|
||||
BOOST_CHECK(addrs[0] == keyaddr[0]);
|
||||
BOOST_CHECK(addrs[1] == keyaddr[1]);
|
||||
BOOST_CHECK(nRequired = 1);
|
||||
BOOST_CHECK(IsMine(keystore, s));
|
||||
BOOST_CHECK(!IsMine(emptykeystore, s));
|
||||
}
|
||||
{
|
||||
vector<vector<pair<opcodetype, valtype> > > solutions;
|
||||
vector<valtype> solutions;
|
||||
txntype whichType;
|
||||
CScript s;
|
||||
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
|
||||
BOOST_CHECK(Solver(s, solutions));
|
||||
BOOST_CHECK(solutions.size() == 3);
|
||||
if (solutions.size() == 3)
|
||||
{
|
||||
BOOST_CHECK(solutions[0].size() == 2);
|
||||
BOOST_CHECK(solutions[1].size() == 2);
|
||||
BOOST_CHECK(solutions[2].size() == 2);
|
||||
}
|
||||
BOOST_CHECK(Solver(s, whichType, solutions));
|
||||
BOOST_CHECK(solutions.size() == 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
203
src/test/script_op_eval_tests.cpp
Normal file
203
src/test/script_op_eval_tests.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/assign/list_of.hpp>
|
||||
#include <boost/assign/list_inserter.hpp>
|
||||
#include <boost/assign/std/vector.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "../main.h"
|
||||
#include "../script.h"
|
||||
#include "../wallet.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Test routines internal to script.cpp:
|
||||
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(script_op_eval_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_op_eval1)
|
||||
{
|
||||
// OP_EVAL looks like this:
|
||||
// scriptSig: <sig> <sig...> <serialized_script>
|
||||
// scriptPubKey: DUP HASH160 <hash> EQUALVERIFY EVAL
|
||||
|
||||
// Test SignSignature() (and therefore the version of Solver() that signs transactions)
|
||||
CBasicKeyStore keystore;
|
||||
CKey key[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
key[i].MakeNewKey();
|
||||
keystore.AddKey(key[i]);
|
||||
}
|
||||
|
||||
// 8 Scripts: checking all combinations of
|
||||
// different keys, straight/EVAL, pubkey/pubkeyhash
|
||||
CScript standardScripts[4];
|
||||
standardScripts[0] << key[0].GetPubKey() << OP_CHECKSIG;
|
||||
standardScripts[1].SetBitcoinAddress(key[1].GetPubKey());
|
||||
standardScripts[2] << key[1].GetPubKey() << OP_CHECKSIG;
|
||||
standardScripts[3].SetBitcoinAddress(key[2].GetPubKey());
|
||||
CScript evalScripts[4];
|
||||
uint160 sigScriptHashes[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
sigScriptHashes[i] = Hash160(standardScripts[i]);
|
||||
keystore.AddCScript(sigScriptHashes[i], standardScripts[i]);
|
||||
evalScripts[i] << OP_DUP << OP_HASH160 << sigScriptHashes[i] << OP_EQUALVERIFY << OP_EVAL;
|
||||
}
|
||||
|
||||
CTransaction txFrom; // Funding transaction:
|
||||
txFrom.vout.resize(8);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
txFrom.vout[i].scriptPubKey = evalScripts[i];
|
||||
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
|
||||
}
|
||||
BOOST_CHECK(txFrom.IsStandard());
|
||||
|
||||
CTransaction txTo[8]; // Spending transactions
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
txTo[i].vin.resize(1);
|
||||
txTo[i].vout.resize(1);
|
||||
txTo[i].vin[0].prevout.n = i;
|
||||
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
||||
txTo[i].vout[0].nValue = 1;
|
||||
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
|
||||
}
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
|
||||
}
|
||||
// All of the above should be OK, and the txTos have valid signatures
|
||||
// Check to make sure signature verification fails if we use the wrong ScriptSig:
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
CScript sigSave = txTo[i].vin[0].scriptSig;
|
||||
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
|
||||
int nUnused = 0;
|
||||
bool sigOK = VerifySignature(txFrom, txTo[i], 0, nUnused);
|
||||
if (i == j)
|
||||
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
|
||||
else
|
||||
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
|
||||
txTo[i].vin[0].scriptSig = sigSave;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_op_eval2)
|
||||
{
|
||||
// Test OP_EVAL edge cases
|
||||
|
||||
CScript recurse;
|
||||
recurse << OP_DUP << OP_EVAL;
|
||||
|
||||
uint160 recurseHash = Hash160(recurse);
|
||||
|
||||
CScript fund;
|
||||
fund << OP_DUP << OP_HASH160 << recurseHash << OP_EQUALVERIFY << OP_EVAL;
|
||||
|
||||
CTransaction txFrom; // Funding transaction:
|
||||
txFrom.vout.resize(1);
|
||||
txFrom.vout[0].scriptPubKey = fund;
|
||||
|
||||
BOOST_CHECK(txFrom.IsStandard()); // Looks like a standard transaction until you try to spend it
|
||||
|
||||
CTransaction txTo;
|
||||
txTo.vin.resize(1);
|
||||
txTo.vout.resize(1);
|
||||
txTo.vin[0].prevout.n = 0;
|
||||
txTo.vin[0].prevout.hash = txFrom.GetHash();
|
||||
txTo.vin[0].scriptSig = CScript() << static_cast<std::vector<unsigned char> >(recurse);
|
||||
txTo.vout[0].nValue = 1;
|
||||
|
||||
int nUnused = 0;
|
||||
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0));
|
||||
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_op_eval3)
|
||||
{
|
||||
// Test the CScript::Set* methods
|
||||
CBasicKeyStore keystore;
|
||||
CKey key[4];
|
||||
std::vector<CKey> keys;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
key[i].MakeNewKey();
|
||||
keystore.AddKey(key[i]);
|
||||
keys.push_back(key[i]);
|
||||
}
|
||||
|
||||
CScript inner[4];
|
||||
inner[0].SetBitcoinAddress(key[0].GetPubKey());
|
||||
inner[1].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+2));
|
||||
inner[2].SetMultisig(1, std::vector<CKey>(keys.begin(), keys.begin()+2));
|
||||
inner[3].SetMultisig(2, std::vector<CKey>(keys.begin(), keys.begin()+3));
|
||||
|
||||
CScript outer[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
outer[i].SetEval(inner[i]);
|
||||
keystore.AddCScript(Hash160(inner[i]), inner[i]);
|
||||
}
|
||||
|
||||
CTransaction txFrom; // Funding transaction:
|
||||
txFrom.vout.resize(4);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
txFrom.vout[i].scriptPubKey = outer[i];
|
||||
}
|
||||
BOOST_CHECK(txFrom.IsStandard());
|
||||
|
||||
CTransaction txTo[4]; // Spending transactions
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
txTo[i].vin.resize(1);
|
||||
txTo[i].vout.resize(1);
|
||||
txTo[i].vin[0].prevout.n = i;
|
||||
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
|
||||
txTo[i].vout[0].nValue = 1;
|
||||
txTo[i].vout[0].scriptPubKey = inner[i];
|
||||
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i));
|
||||
BOOST_CHECK_MESSAGE(txTo[i].IsStandard(), strprintf("txTo[%d].IsStandard", i));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_op_eval_backcompat)
|
||||
{
|
||||
// Check backwards-incompatibility-testing code
|
||||
CScript returnsEleven;
|
||||
returnsEleven << OP_11;
|
||||
|
||||
// This will validate on new clients, but will
|
||||
// be invalid on old clients (that interpret OP_EVAL as a no-op)
|
||||
CScript fund;
|
||||
fund << OP_EVAL << OP_11 << OP_EQUAL;
|
||||
|
||||
CTransaction txFrom; // Funding transaction:
|
||||
txFrom.vout.resize(1);
|
||||
txFrom.vout[0].scriptPubKey = fund;
|
||||
|
||||
CTransaction txTo;
|
||||
txTo.vin.resize(1);
|
||||
txTo.vout.resize(1);
|
||||
txTo.vin[0].prevout.n = 0;
|
||||
txTo.vin[0].prevout.hash = txFrom.GetHash();
|
||||
txTo.vin[0].scriptSig = CScript() << static_cast<std::vector<unsigned char> >(returnsEleven);
|
||||
txTo.vout[0].nValue = 1;
|
||||
|
||||
int nUnused = 0;
|
||||
BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0));
|
||||
BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -7,7 +7,7 @@
|
||||
|
||||
using namespace std;
|
||||
extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType);
|
||||
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(script_tests)
|
||||
@ -21,19 +21,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
|
||||
static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a };
|
||||
static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };
|
||||
|
||||
int nUnused = 0;
|
||||
|
||||
vector<vector<unsigned char> > directStack;
|
||||
BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0));
|
||||
BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, nUnused));
|
||||
|
||||
vector<vector<unsigned char> > pushdata1Stack;
|
||||
BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0));
|
||||
BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, nUnused));
|
||||
BOOST_CHECK(pushdata1Stack == directStack);
|
||||
|
||||
vector<vector<unsigned char> > pushdata2Stack;
|
||||
BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0));
|
||||
BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, nUnused));
|
||||
BOOST_CHECK(pushdata2Stack == directStack);
|
||||
|
||||
vector<vector<unsigned char> > pushdata4Stack;
|
||||
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0));
|
||||
BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, nUnused));
|
||||
BOOST_CHECK(pushdata4Stack == directStack);
|
||||
}
|
||||
|
||||
@ -71,6 +73,7 @@ sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
|
||||
{
|
||||
int nUnused = 0;
|
||||
CKey key1, key2, key3;
|
||||
key1.MakeNewKey();
|
||||
key2.MakeNewKey();
|
||||
@ -91,19 +94,20 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
|
||||
txTo12.vout[0].nValue = 1;
|
||||
|
||||
CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
|
||||
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0));
|
||||
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
|
||||
txTo12.vout[0].nValue = 2;
|
||||
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
|
||||
|
||||
CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
|
||||
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, 0));
|
||||
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0));
|
||||
|
||||
CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
|
||||
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
|
||||
{
|
||||
int nUnused = 0;
|
||||
CKey key1, key2, key3, key4;
|
||||
key1.MakeNewKey();
|
||||
key2.MakeNewKey();
|
||||
@ -127,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
|
||||
std::vector<CKey> keys;
|
||||
keys.push_back(key1); keys.push_back(key2);
|
||||
CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key1); keys.push_back(key3);
|
||||
CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key2); keys.push_back(key3);
|
||||
CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
|
||||
CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
|
||||
CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
|
||||
CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
|
||||
CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear();
|
||||
keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
|
||||
CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
|
||||
keys.clear(); // Must have signatures
|
||||
CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
|
||||
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, 0));
|
||||
BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0));
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,6 +42,15 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CWallet::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
|
||||
{
|
||||
if (!CCryptoKeyStore::AddCScript(hash, data))
|
||||
return false;
|
||||
if (!fFileBacked)
|
||||
return true;
|
||||
return CWalletDB(strWalletFile).WriteCScript(hash, data);
|
||||
}
|
||||
|
||||
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
|
||||
{
|
||||
if (!IsLocked())
|
||||
@ -374,6 +383,16 @@ int64 CWallet::GetDebit(const CTxIn &txin) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CWallet::IsChange(const CTxOut& txout) const
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
if (ExtractAddress(txout.scriptPubKey, this, address) && !address.IsScript())
|
||||
CRITICAL_BLOCK(cs_wallet)
|
||||
if (!mapAddressBook.count(address))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int64 CWalletTx::GetTxTime() const
|
||||
{
|
||||
return nTimeReceived;
|
||||
@ -443,8 +462,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
|
||||
nFee = nDebit - nValueOut;
|
||||
}
|
||||
|
||||
// Sent/received. Standard client will never generate a send-to-multiple-recipients,
|
||||
// but non-standard clients might (so return a list of address/amount pairs)
|
||||
// Sent/received.
|
||||
BOOST_FOREACH(const CTxOut& txout, vout)
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
|
12
src/wallet.h
12
src/wallet.h
@ -69,6 +69,8 @@ public:
|
||||
bool AddCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
|
||||
// Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
|
||||
bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
|
||||
bool AddCScript(const uint160& hash, const std::vector<unsigned char>& data);
|
||||
bool LoadCScript(const uint160& hash, const std::vector<unsigned char>& data) { return CCryptoKeyStore::AddCScript(hash, data); }
|
||||
|
||||
bool Unlock(const SecureString& strWalletPassphrase);
|
||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||
@ -114,15 +116,7 @@ public:
|
||||
throw std::runtime_error("CWallet::GetCredit() : value out of range");
|
||||
return (IsMine(txout) ? txout.nValue : 0);
|
||||
}
|
||||
bool IsChange(const CTxOut& txout) const
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
if (ExtractAddress(txout.scriptPubKey, this, address))
|
||||
CRITICAL_BLOCK(cs_wallet)
|
||||
if (!mapAddressBook.count(address))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool IsChange(const CTxOut& txout) const;
|
||||
int64 GetChange(const CTxOut& txout) const
|
||||
{
|
||||
if (!MoneyRange(txout.nValue))
|
||||
|
Loading…
Reference in New Issue
Block a user