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