Browse Source

OP_EVAL implementation

OP_EVAL is a new opcode that evaluates an item on the stack as a script.
It enables a new type of bitcoin address that needs an arbitrarily
complex script to redeem.
tags/v0.15.1
Gavin Andresen 9 years ago
parent
commit
e679ec969c
15 changed files with 1155 additions and 572 deletions
  1. 25
    0
      src/base58.h
  2. 52
    70
      src/bitcoinrpc.cpp
  3. 9
    0
      src/db.cpp
  4. 12
    0
      src/db.h
  5. 30
    0
      src/keystore.cpp
  6. 9
    0
      src/keystore.h
  7. 171
    59
      src/main.cpp
  8. 7
    29
      src/main.h
  9. 488
    168
      src/script.cpp
  10. 52
    170
      src/script.h
  11. 52
    47
      src/test/multisig_tests.cpp
  12. 203
    0
      src/test/script_op_eval_tests.cpp
  13. 22
    18
      src/test/script_tests.cpp
  14. 20
    2
      src/wallet.cpp
  15. 3
    9
      src/wallet.h

+ 25
- 0
src/base58.h View File

@@ -268,6 +268,12 @@ public:
SetHash160(Hash160(vchPubKey));
}

bool SetScriptHash160(const uint160& hash160)
{
SetData(fTestNet ? 112 : 1, &hash160, 20);
return true;
}

bool IsValid() const
{
int nExpectedSize = 20;
@@ -275,9 +281,20 @@ public:
switch(nVersion)
{
case 0:
nExpectedSize = 20; // Hash of public key
fExpectTestNet = false;
break;
case 1:
nExpectedSize = 20; // OP_EVAL, hash of CScript
fExpectTestNet = false;
break;

case 111:
nExpectedSize = 20;
fExpectTestNet = true;
break;
case 112:
nExpectedSize = 20;
fExpectTestNet = true;
break;

@@ -286,6 +303,14 @@ public:
}
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
}
bool IsScript() const
{
if (!IsValid())
return false;
if (fTestNet)
return nVersion == 112;
return nVersion == 1;
}

CBitcoinAddress()
{

+ 52
- 70
src/bitcoinrpc.cpp View File

@@ -667,7 +667,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
if (params.size() > 1)
nMinDepth = params[1].get_int();

// Get the set of pub keys that have the label
// Get the set of pub keys assigned to account
string strAccount = AccountFromValue(params[0]);
set<CBitcoinAddress> setAddress;
GetAccountAddresses(strAccount, setAddress);
@@ -936,56 +936,30 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex();
}

Value sendmultisig(const Array& params, bool fHelp)
Value addmultisigaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 4 || params.size() > 7)
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "sendmultisig <fromaccount> <type> <[\"key\",\"key\"]> <amount> [minconf=1] [comment] [comment-to]\n"
"<type> is one of: \"and\", \"or\", \"escrow\"\n"
"<keys> is an array of strings (in JSON array format); each key is a bitcoin address, hex or base58 public key\n"
"<amount> is a real and is rounded to the nearest 0.00000001";
if (pwalletMain->IsCrypted())
msg += "\nrequires wallet passphrase to be set with walletpassphrase first";
string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
"Add a nrequired-to-sign multisignature address to the wallet\"\n"
"each key is a bitcoin address, hex or base58 public key\n"
"If [account] is specified, assign address to [account].";
throw runtime_error(msg);
}

string strAccount = AccountFromValue(params[0]);
string strType = params[1].get_str();
const Array& keys = params[2].get_array();
int64 nAmount = AmountFromValue(params[3]);
int nMinDepth = 1;
if (params.size() > 4)
nMinDepth = params[4].get_int();

CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
wtx.mapValue["comment"] = params[5].get_str();
if (params.size() > 6 && params[6].type() != null_type && !params[6].get_str().empty())
wtx.mapValue["to"] = params[6].get_str();

if (pwalletMain->IsLocked())
throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");

// Check funds
int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
if (nAmount > nBalance)
throw JSONRPCError(-6, "Account has insufficient funds");
int nRequired = params[0].get_int();
const Array& keys = params[1].get_array();
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);

// Gather public keys
int nKeysNeeded = 0;
if (strType == "and" || strType == "or")
nKeysNeeded = 2;
else if (strType == "escrow")
nKeysNeeded = 3;
else
throw runtime_error("sendmultisig: <type> must be one of: and or and_or");
if (keys.size() != nKeysNeeded)
if (keys.size() < nRequired)
throw runtime_error(
strprintf("sendmultisig: wrong number of keys (got %d, need %d)", keys.size(), nKeysNeeded));
strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
std::vector<CKey> pubkeys;
pubkeys.resize(nKeysNeeded);
for (int i = 0; i < nKeysNeeded; i++)
pubkeys.resize(keys.size());
for (int i = 0; i < keys.size(); i++)
{
const std::string& ks = keys[i].get_str();
if (ks.size() == 130) // hex public key
@@ -1003,32 +977,23 @@ Value sendmultisig(const Array& params, bool fHelp)
CBitcoinAddress address(ks);
if (!pwalletMain->GetKey(address, pubkeys[i]))
throw runtime_error(
strprintf("sendmultisig: unknown address: %s",ks.c_str()));
strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
}
}

// Send
CScript scriptPubKey;
if (strType == "and")
scriptPubKey.SetMultisigAnd(pubkeys);
else if (strType == "or")
scriptPubKey.SetMultisigOr(pubkeys);
else
scriptPubKey.SetMultisigEscrow(pubkeys);
// Construct using OP_EVAL
CScript inner;
inner.SetMultisig(nRequired, pubkeys);

CReserveKey keyChange(pwalletMain);
int64 nFeeRequired = 0;
bool fCreated = pwalletMain->CreateTransaction(scriptPubKey, nAmount, wtx, keyChange, nFeeRequired);
if (!fCreated)
{
if (nAmount + nFeeRequired > pwalletMain->GetBalance())
throw JSONRPCError(-6, "Insufficient funds");
throw JSONRPCError(-4, "Transaction creation failed");
}
if (!pwalletMain->CommitTransaction(wtx, keyChange))
throw JSONRPCError(-4, "Transaction commit failed");
uint160 scriptHash = Hash160(inner);
CScript scriptPubKey;
scriptPubKey.SetEval(inner);
pwalletMain->AddCScript(scriptHash, inner);
CBitcoinAddress address;
address.SetScriptHash160(scriptHash);

return wtx.GetHash().GetHex();
pwalletMain->SetAddressBookName(address, strAccount);
return address.ToString();
}


@@ -1700,6 +1665,24 @@ Value validateaddress(const Array& params, bool fHelp)
std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey)));
}
else if (pwalletMain->HaveCScript(address.GetHash160()))
{
ret.push_back(Pair("isscript", true));
CScript subscript;
pwalletMain->GetCScript(address.GetHash160(), subscript);
ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
std::vector<CBitcoinAddress> addresses;
txntype whichType;
int nRequired;
ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
ret.push_back(Pair("script", GetTxnTypeName(whichType)));
Array a;
BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
a.push_back(addr.ToString());
ret.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
ret.push_back(Pair("sigsrequired", nRequired));
}
else
ret.push_back(Pair("ismine", false));
if (pwalletMain->mapAddressBook.count(address))
@@ -1946,7 +1929,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany),
make_pair("sendmultisig", &sendmultisig),
make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage),
@@ -2590,16 +2573,15 @@ int CommandLineRPC(int argc, char *argv[])
params[1] = v.get_obj();
}
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "sendmultisig" && n > 2)
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1)
{
string s = params[2].get_str();
string s = params[1].get_str();
Value v;
if (!read_string(s, v) || v.type() != array_type)
throw runtime_error("sendmultisig: type mismatch "+s);
params[2] = v.get_array();
throw runtime_error("addmultisigaddress: type mismatch "+s);
params[1] = v.get_array();
}
if (strMethod == "sendmultisig" && n > 3) ConvertTo<double>(params[3]);
if (strMethod == "sendmultisig" && n > 4) ConvertTo<boost::int64_t>(params[4]);

// Execute
Object reply = CallRPC(strMethod, params);

+ 9
- 0
src/db.cpp View File

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

+ 12
- 0
src/db.h View File

@@ -420,6 +420,18 @@ public:
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}

bool ReadCScript(const uint160 &hash, std::vector<unsigned char>& data)
{
data.clear();
return Read(std::make_pair(std::string("cscript"), hash), data);
}

bool WriteCScript(const uint160& hash, const std::vector<unsigned char>& data)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("cscript"), hash), data, false);
}

bool WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;

+ 30
- 0
src/keystore.cpp View File

@@ -33,6 +33,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
return true;
}

bool CBasicKeyStore::AddCScript(const uint160 &hash, const std::vector<unsigned char>& data)
{
CRITICAL_BLOCK(cs_KeyStore)
mapData[hash] = data;
return true;
}

bool CBasicKeyStore::HaveCScript(const uint160& hash) const
{
bool result;
CRITICAL_BLOCK(cs_KeyStore)
result = (mapData.count(hash) > 0);
return result;
}


bool CBasicKeyStore::GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const
{
CRITICAL_BLOCK(cs_KeyStore)
{
DataMap::const_iterator mi = mapData.find(hash);
if (mi != mapData.end())
{
dataOut = (*mi).second;
return true;
}
}
return false;
}

bool CCryptoKeyStore::SetCrypted()
{
CRITICAL_BLOCK(cs_KeyStore)

+ 9
- 0
src/keystore.h View File

@@ -31,6 +31,10 @@ public:
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;

virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data) =0;
virtual bool HaveCScript(const uint160 &hash) const =0;
virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const =0;

// Generate a new key, and add it to the store
virtual std::vector<unsigned char> GenerateNewKey();
virtual bool GetSecret(const CBitcoinAddress &address, CSecret& vchSecret) const
@@ -44,12 +48,14 @@ public:
};

typedef std::map<CBitcoinAddress, CSecret> KeyMap;
typedef std::map<uint160, std::vector<unsigned char> > DataMap;

// Basic key store, that keeps keys in an address->secret map
class CBasicKeyStore : public CKeyStore
{
protected:
KeyMap mapKeys;
DataMap mapData;

public:
bool AddKey(const CKey& key);
@@ -86,6 +92,9 @@ public:
}
return false;
}
virtual bool AddCScript(const uint160 &hash, const std::vector<unsigned char>& data);
virtual bool HaveCScript(const uint160 &hash) const;
virtual bool GetCScript(const uint160 &hash, std::vector<unsigned char>& dataOut) const;
};

typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;

+ 171
- 59
src/main.cpp View File

@@ -246,6 +246,65 @@ bool CTransaction::ReadFromDisk(COutPoint prevout)
return ReadFromDisk(txdb, prevout, txindex);
}

bool CTransaction::IsStandard() const
{
BOOST_FOREACH(const CTxIn& txin, vin)
{
// Biggest 'standard' txin is a 2-signature 2-of-3 escrow
// in an OP_EVAL, which is 2 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
if (txin.scriptSig.size() > 400)
return error("nonstandard txin, size %d\n", txin.scriptSig.size());
if (!txin.scriptSig.IsPushOnly())
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
}
BOOST_FOREACH(const CTxOut& txout, vout)
if (!::IsStandard(txout.scriptPubKey))
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
return true;
}

//
// Check transaction inputs, and make sure any
// OP_EVAL transactions are evaluating IsStandard scripts
//
// Why bother? To avoid denial-of-service attacks; an attacker
// can submit a standard DUP HASH... OP_EVAL transaction,
// which will get accepted into blocks. The script being
// EVAL'ed can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
bool CTransaction::IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
{
if (fTestNet)
return true; // Allow non-standard on testnet

for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
assert(mapInputs.count(prevout.hash) > 0);
CTransaction& txPrev = mapInputs[prevout.hash].second;

vector<vector<unsigned char> > vSolutions;
txntype whichType;
if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions))
return false;
if (whichType == TX_SCRIPTHASH)
{
vector<vector<unsigned char> > stack;
int nUnused;
if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused))
return false;
const vector<unsigned char>& subscript = stack.back();
if (!::IsStandard(CScript(subscript.begin(), subscript.end())))
return false;
}
}

return true;
}



int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@@ -369,15 +428,6 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
if ((int64)nLockTime > INT_MAX)
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");

// Safety limits
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 34 bytes.
// 34 bytes because a TxOut is:
// 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
if (GetSigOpCount() > nSize / 34 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");

// Rather not work on nonstandard transactions (unless -testnet)
if (!fTestNet && !IsStandard())
return error("AcceptToMemoryPool() : nonstandard transaction type");
@@ -421,15 +471,34 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi

if (fCheckInputs)
{
// Check against previous transactions
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
map<uint256, CTxIndex> mapUnused;
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
{
if (pfMissingInputs)
*pfMissingInputs = true;
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
}

// Check for non-standard OP_EVALs in inputs
if (!IsStandardInputs(mapInputs))
return error("AcceptToMemoryPool() : nonstandard transaction input");

// Check against previous transactions
int64 nFees = 0;
if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
int nSigOps = 0;
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
{
if (pfMissingInputs)
*pfMissingInputs = true;
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
// Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
// attacks disallow transactions with more than one SigOp per 65 bytes.
// 65 bytes because that is the minimum size of an ECDSA signature
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
if (nSigOps > nSize / 65 || nSize < 100)
return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");

// Don't accept it if it can't get into a block
if (nFees < GetMinFee(1000, true, true))
@@ -826,8 +895,61 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb)
}


bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
{
if (IsCoinBase())
return true; // Coinbase transactions have no inputs to fetch.
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;
if (inputsRet.count(prevout.hash))
continue; // Got it already

// Read txindex
CTxIndex& txindex = inputsRet[prevout.hash].first;
bool fFound = true;
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
{
// Get txindex from current proposed changes
txindex = mapTestPool.find(prevout.hash)->second;
}
else
{
// Read txindex from txdb
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
}
if (!fFound && (fBlock || fMiner))
return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());

// Read txPrev
CTransaction& txPrev = inputsRet[prevout.hash].second;
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
{
// Get prev tx from single transactions in memory
CRITICAL_BLOCK(cs_mapTransactions)
{
if (!mapTransactions.count(prevout.hash))
return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
txPrev = mapTransactions[prevout.hash];
}
if (!fFound)
txindex.vSpent.resize(txPrev.vout.size());
}
else
{
// Get prev tx from disk
if (!txPrev.ReadFromDisk(txindex.pos))
return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
}
}
return true;
}

bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
{
// Take over previous transactions' spent pointers
// fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
@@ -839,43 +961,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
for (int i = 0; i < vin.size(); i++)
{
COutPoint prevout = vin[i].prevout;

// Read txindex
CTxIndex txindex;
bool fFound = true;
if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
{
// Get txindex from current proposed changes
txindex = mapTestPool[prevout.hash];
}
else
{
// Read txindex from txdb
fFound = txdb.ReadTxIndex(prevout.hash, txindex);
}
if (!fFound && (fBlock || fMiner))
return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());

// Read txPrev
CTransaction txPrev;
if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
{
// Get prev tx from single transactions in memory
CRITICAL_BLOCK(cs_mapTransactions)
{
if (!mapTransactions.count(prevout.hash))
return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
txPrev = mapTransactions[prevout.hash];
}
if (!fFound)
txindex.vSpent.resize(txPrev.vout.size());
}
else
{
// Get prev tx from disk
if (!txPrev.ReadFromDisk(txindex.pos))
return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
}
assert(inputs.count(prevout.hash) > 0);
CTxIndex& txindex = inputs[prevout.hash].first;
CTransaction& txPrev = inputs[prevout.hash].second;

if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
@@ -891,7 +979,7 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPoo
// still computed and checked, and any change will be caught at the next checkpoint.
if (!(fBlock && IsInitialBlockDownload()))
// Verify signature
if (!VerifySignature(txPrev, *this, i))
if (!VerifySignature(txPrev, *this, i, nSigOpsRet))
return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));

// Check for conflicts (double-spend)
@@ -965,7 +1053,8 @@ bool CTransaction::ClientConnectInputs()
return false;

// Verify signature
if (!VerifySignature(txPrev, *this, i))
int nUnused = 0;
if (!VerifySignature(txPrev, *this, i, nUnused))
return error("ConnectInputs() : VerifySignature failed");

///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@ -1023,14 +1112,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)

map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
nTxPos += ::GetSerializeSize(tx, SER_DISK);

if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false))
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
return false;
if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps))
return false;
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
}

// Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
@@ -1291,8 +1387,21 @@ bool CBlock::CheckBlock() const
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));

// Check that it's not full of nonstandard transactions
if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
// This code should be removed when a compatibility-breaking block chain split has passed.
// Compatibility check for old clients that counted sigops differently:
int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nSigOps += txin.scriptSig.GetSigOpCount();
}
BOOST_FOREACH(const CTxOut& txout, tx.vout)
{
nSigOps += txout.scriptPubKey.GetSigOpCount();
}
}
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));

// Check merkleroot
@@ -2827,9 +2936,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
continue;
int nTxSigOps = tx.GetSigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;

// Transaction fee required depends on block size
bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
@@ -2838,7 +2944,13 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
// Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
continue;
int nTxSigOps = 0;
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
continue;
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
swap(mapTestPool, mapTestPoolTmp);


+ 7
- 29
src/main.h View File

@@ -493,26 +493,8 @@ public:
return (vin.size() == 1 && vin[0].prevout.IsNull());
}

int GetSigOpCount() const
{
int n = 0;
BOOST_FOREACH(const CTxIn& txin, vin)
n += txin.scriptSig.GetSigOpCount();
BOOST_FOREACH(const CTxOut& txout, vout)
n += txout.scriptPubKey.GetSigOpCount();
return n;
}

bool IsStandard() const
{
BOOST_FOREACH(const CTxIn& txin, vin)
if (!txin.scriptSig.IsPushOnly())
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
BOOST_FOREACH(const CTxOut& txout, vout)
if (!::IsStandard(txout.scriptPubKey))
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
return true;
}
bool IsStandard() const;
bool IsStandardInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;

int64 GetValueOut() const
{
@@ -640,8 +622,11 @@ public:
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
bool ReadFromDisk(COutPoint prevout);
bool DisconnectInputs(CTxDB& txdb);
bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
bool fBlock, bool fMiner, std::map<uint256, std::pair<CTxIndex, CTransaction> >& inputsRet);
bool ConnectInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > inputs,
std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0);
bool ClientConnectInputs();
bool CheckTransaction() const;
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
@@ -851,13 +836,6 @@ public:
return (int64)nTime;
}

int GetSigOpCount() const
{
int n = 0;
BOOST_FOREACH(const CTransaction& tx, vtx)
n += tx.GetSigOpCount();
return n;
}


uint256 BuildMerkleTree() const

+ 488
- 168
src/script.cpp View File

@@ -70,20 +70,186 @@ static inline void popstack(vector<valtype>& stack)
}


bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
const char* GetTxnTypeName(txntype t)
{
switch (t)
{
case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey";
case TX_PUBKEYHASH: return "pubkeyhash";
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
}
return NULL;
}


const char* GetOpName(opcodetype opcode)
{
switch (opcode)
{
// push value
case OP_0 : return "0";
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
case OP_1NEGATE : return "-1";
case OP_RESERVED : return "OP_RESERVED";
case OP_1 : return "1";
case OP_2 : return "2";
case OP_3 : return "3";
case OP_4 : return "4";
case OP_5 : return "5";
case OP_6 : return "6";
case OP_7 : return "7";
case OP_8 : return "8";
case OP_9 : return "9";
case OP_10 : return "10";
case OP_11 : return "11";
case OP_12 : return "12";
case OP_13 : return "13";
case OP_14 : return "14";
case OP_15 : return "15";
case OP_16 : return "16";

// control
case OP_NOP : return "OP_NOP";
case OP_VER : return "OP_VER";
case OP_IF : return "OP_IF";
case OP_NOTIF : return "OP_NOTIF";
case OP_VERIF : return "OP_VERIF";
case OP_VERNOTIF : return "OP_VERNOTIF";
case OP_ELSE : return "OP_ELSE";
case OP_ENDIF : return "OP_ENDIF";
case OP_VERIFY : return "OP_VERIFY";
case OP_RETURN : return "OP_RETURN";

// stack ops
case OP_TOALTSTACK : return "OP_TOALTSTACK";
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
case OP_2DROP : return "OP_2DROP";
case OP_2DUP : return "OP_2DUP";
case OP_3DUP : return "OP_3DUP";
case OP_2OVER : return "OP_2OVER";
case OP_2ROT : return "OP_2ROT";
case OP_2SWAP : return "OP_2SWAP";
case OP_IFDUP : return "OP_IFDUP";
case OP_DEPTH : return "OP_DEPTH";
case OP_DROP : return "OP_DROP";
case OP_DUP : return "OP_DUP";
case OP_NIP : return "OP_NIP";
case OP_OVER : return "OP_OVER";
case OP_PICK : return "OP_PICK";
case OP_ROLL : return "OP_ROLL";
case OP_ROT : return "OP_ROT";
case OP_SWAP : return "OP_SWAP";
case OP_TUCK : return "OP_TUCK";

// splice ops
case OP_CAT : return "OP_CAT";
case OP_SUBSTR : return "OP_SUBSTR";
case OP_LEFT : return "OP_LEFT";
case OP_RIGHT : return "OP_RIGHT";
case OP_SIZE : return "OP_SIZE";

// bit logic
case OP_INVERT : return "OP_INVERT";
case OP_AND : return "OP_AND";
case OP_OR : return "OP_OR";
case OP_XOR : return "OP_XOR";
case OP_EQUAL : return "OP_EQUAL";
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
case OP_RESERVED1 : return "OP_RESERVED1";
case OP_RESERVED2 : return "OP_RESERVED2";

// numeric
case OP_1ADD : return "OP_1ADD";
case OP_1SUB : return "OP_1SUB";
case OP_2MUL : return "OP_2MUL";
case OP_2DIV : return "OP_2DIV";
case OP_NEGATE : return "OP_NEGATE";
case OP_ABS : return "OP_ABS";
case OP_NOT : return "OP_NOT";
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
case OP_ADD : return "OP_ADD";
case OP_SUB : return "OP_SUB";
case OP_MUL : return "OP_MUL";
case OP_DIV : return "OP_DIV";
case OP_MOD : return "OP_MOD";
case OP_LSHIFT : return "OP_LSHIFT";
case OP_RSHIFT : return "OP_RSHIFT";
case OP_BOOLAND : return "OP_BOOLAND";
case OP_BOOLOR : return "OP_BOOLOR";
case OP_NUMEQUAL : return "OP_NUMEQUAL";
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
case OP_LESSTHAN : return "OP_LESSTHAN";
case OP_GREATERTHAN : return "OP_GREATERTHAN";
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
case OP_MIN : return "OP_MIN";
case OP_MAX : return "OP_MAX";
case OP_WITHIN : return "OP_WITHIN";

// crypto
case OP_RIPEMD160 : return "OP_RIPEMD160";
case OP_SHA1 : return "OP_SHA1";
case OP_SHA256 : return "OP_SHA256";
case OP_HASH160 : return "OP_HASH160";
case OP_HASH256 : return "OP_HASH256";
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
case OP_CHECKSIG : return "OP_CHECKSIG";
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";

// meta
case OP_EVAL : return "OP_EVAL";

// expanson
case OP_NOP2 : return "OP_NOP2";
case OP_NOP3 : return "OP_NOP3";
case OP_NOP4 : return "OP_NOP4";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8";
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";



// template matching params
case OP_SCRIPTHASH : return "OP_SCRIPTHASH";
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY";

case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
return "OP_UNKNOWN";
}
}


//
// Returns true if script is valid.
//
bool EvalScriptInner(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType,
CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, int nRecurseDepth)
{
CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
opcodetype opcode;
valtype vchPushValue;
vector<bool> vfExec;
vector<valtype> altstack;
if (script.size() > 10000)
return false;
int nOpCount = 0;

// Limit OP_EVAL recursion
if (nRecurseDepth > 2)
return false;

try
{
@@ -155,7 +321,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Control
//
case OP_NOP:
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
break;

@@ -751,12 +917,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
//PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n");

// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
CScript scriptCode(pbegincodehash, pendcodehash);

// Drop the signature, since there's no way for a signature to sign itself
scriptCode.FindAndDelete(CScript(vchSig));

bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
nSigOpCount++;

popstack(stack);
popstack(stack);
@@ -800,7 +967,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return false;

// Subset of script starting at the most recent codeseparator
CScript scriptCode(pbegincodehash, pend);
CScript scriptCode(pbegincodehash, pendcodehash);

// Drop the signatures, since there's no way for a signature to sign itself
for (int k = 0; k < nSigsCount; k++)
@@ -823,6 +990,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}
ikey++;
nKeysCount--;
nSigOpCount++;

// If there are more signatures left than keys left,
// then too many signatures have failed
@@ -844,6 +1012,26 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
}
break;

case OP_EVAL:
{
// Evaluate the top item on the stack as a Script
// [serialized script ] -- [result(s) of executing script]
if (stack.size() < 1)
return false;
valtype& vchScript = stacktop(-1);
CScript subscript(vchScript.begin(), vchScript.end());
popstack(stack);
// Codeseparators not allowed
if (subscript.Find(OP_CODESEPARATOR))
return false;

if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType,
pbegincodehash, pendcodehash, nOpCount, nSigOpCount, nRecurseDepth++))
return false;
}
break;

default:
return false;
}
@@ -865,6 +1053,17 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
return true;
}

bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script,
const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet)
{
CScript::const_iterator pbegincodehash = script.begin();
CScript::const_iterator pendcodehash = script.end();

int nOpCount = 0;
return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash,
nOpCount, nSigOpCountRet, 0);
}




@@ -964,38 +1163,35 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc


//
// Returns lists of public keys (or public key hashes), any one of which can
// satisfy scriptPubKey
// Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
//
bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet)
bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector<vector<unsigned char> >& vSolutionsRet)
{
// Templates
static vector<CScript> vTemplates;
if (vTemplates.empty())
static map<txntype, CScript> mTemplates;
if (mTemplates.empty())
{
// Standard tx, sender provides pubkey, receiver adds signature
vTemplates.push_back(CScript() << OP_PUBKEY << OP_CHECKSIG);
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));

// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
vTemplates.push_back(CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG);

// Sender provides two pubkeys, receivers provides two signatures
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));

// Sender provides two pubkeys, receivers provides one of two signatures
vTemplates.push_back(CScript() << OP_1 << OP_PUBKEY << OP_PUBKEY << OP_2 << OP_CHECKMULTISIG);
// Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));

// Sender provides three pubkeys, receiver provides 2 of 3 signatures.
vTemplates.push_back(CScript() << OP_2 << OP_PUBKEY << OP_PUBKEY << OP_PUBKEY << OP_3 << OP_CHECKMULTISIG);
// Sender provides script hash, receiver provides script and
// as many signatures as required to satisfy script
mTemplates.insert(make_pair(TX_SCRIPTHASH, CScript() << OP_DUP << OP_HASH160 << OP_SCRIPTHASH << OP_EQUALVERIFY << OP_EVAL));
}

// Scan templates
const CScript& script1 = scriptPubKey;
BOOST_FOREACH(const CScript& script2, vTemplates)
BOOST_FOREACH(const PAIRTYPE(txntype, CScript)& tplate, mTemplates)
{
const CScript& script2 = tplate.second;
vSolutionsRet.clear();

vector<pair<opcodetype, valtype> > currentSolution;
opcodetype opcode1, opcode2;
vector<unsigned char> vch1, vch2;

@@ -1006,218 +1202,333 @@ bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype>
{
if (pc1 == script1.end() && pc2 == script2.end())
{
return !vSolutionsRet.empty();
// Found a match
typeRet = tplate.first;
if (typeRet == TX_MULTISIG)
{
// Additional checks for TX_MULTISIG:
unsigned char m = vSolutionsRet.front()[0];
unsigned char n = vSolutionsRet.back()[0];
if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
return false;
}
return true;
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
break;

// Template matching opcodes:
if (opcode2 == OP_PUBKEYS)
{
while (vch1.size() >= 33 && vch1.size() <= 120)
{
vSolutionsRet.push_back(vch1);
if (!script1.GetOp(pc1, opcode1, vch1))
break;
}
if (!script2.GetOp(pc2, opcode2, vch2))
break;
// Normal situation is to fall through
// to other if/else statments
}

if (opcode2 == OP_PUBKEY)
{
if (vch1.size() < 33 || vch1.size() > 120)
break;
currentSolution.push_back(make_pair(opcode2, vch1));
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_PUBKEYHASH)
{
if (vch1.size() != sizeof(uint160))
break;
currentSolution.push_back(make_pair(opcode2, vch1));
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_CHECKSIG)
else if (opcode2 == OP_SCRIPTHASH)
{
vSolutionsRet.push_back(currentSolution);
currentSolution.clear();
if (vch1.size() != sizeof(uint160))
break;
vSolutionsRet.push_back(vch1);
}
else if (opcode2 == OP_CHECKMULTISIG)
{ // Dig out the "m" from before the pubkeys:
CScript::const_iterator it = script2.begin();
opcodetype op_m;
script2.GetOp(it, op_m, vch1);
int m = CScript::DecodeOP_N(op_m);
int n = currentSolution.size();

if (m == 2 && n == 2)
else if (opcode2 == OP_SMALLINTEGER)
{ // Single-byte small integer pushed onto vSolutions
if (opcode1 == OP_0 ||
(opcode1 >= OP_1 && opcode1 <= OP_16))
{
vSolutionsRet.push_back(currentSolution);
currentSolution.clear();
}
else if (m == 1 && n == 2)
{ // 2 solutions: either first key or second
for (int i = 0; i < 2; i++)
{
vector<pair<opcodetype, valtype> > s;
s.push_back(currentSolution[i]);
vSolutionsRet.push_back(s);
}
currentSolution.clear();
}
else if (m == 2 && n == 3)
{ // 3 solutions: any pair
for (int i = 0; i < 2; i++)
for (int j = i+1; j < 3; j++)
{
vector<pair<opcodetype, valtype> > s;
s.push_back(currentSolution[i]);
s.push_back(currentSolution[j]);
vSolutionsRet.push_back(s);
}
currentSolution.clear();
char n = (char)CScript::DecodeOP_N(opcode1);
vSolutionsRet.push_back(valtype(1, n));
}
else
break;
}
else if (opcode1 != opcode2 || vch1 != vch2)
{
// Others must match exactly
break;
}
}
}

vSolutionsRet.clear();
typeRet = TX_NONSTANDARD;
return false;
}


bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
{
scriptSigRet.clear();
CKey key;
if (!keystore.GetKey(address, key))
return false;

vector<vector<pair<opcodetype, valtype> > > vSolutions;
if (!Solver(scriptPubKey, vSolutions))
vector<unsigned char> vchSig;
if (!key.Sign(hash, vchSig))
return false;
vchSig.push_back((unsigned char)nHashType);
scriptSigRet << vchSig;

// See if we have all the keys for any of the solutions:
int whichSolution = -1;
for (int i = 0; i < vSolutions.size(); i++)
{
int keysFound = 0;
CScript scriptSig;
return true;
}

BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
{
if (item.first == OP_PUBKEY)
{
const valtype& vchPubKey = item.second;
CKey key;
vector<unsigned char> vchSig;
if (keystore.GetKey(Hash160(vchPubKey), key) && key.GetPubKey() == vchPubKey
&& hash != 0 && key.Sign(hash, vchSig))
{
vchSig.push_back((unsigned char)nHashType);
scriptSig << vchSig;
++keysFound;
}
}
else if (item.first == OP_PUBKEYHASH)
{
CKey key;
vector<unsigned char> vchSig;
if (keystore.GetKey(uint160(item.second), key)
&& hash != 0 && key.Sign(hash, vchSig))
{
vchSig.push_back((unsigned char)nHashType);
scriptSig << vchSig << key.GetPubKey();
++keysFound;
}
}
}
if (keysFound == vSolutions[i].size())
bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
{
int nSigned = 0;
int nRequired = multisigdata.front()[0];
for (vector<valtype>::const_iterator it = multisigdata.begin()+1; it != multisigdata.begin()+multisigdata.size()-1; it++)
{
const valtype& pubkey = *it;
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (Sign1(address, keystore, hash, nHashType, scriptSigRet))
{
whichSolution = i;
scriptSigRet = scriptSig;
break;
++nSigned;
if (nSigned == nRequired) break;
}
}
if (whichSolution == -1)
return nSigned==nRequired;
}

//
// Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type.
// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed).
// Returns true if scriptPubKey could be completely satisified.
//
bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet)
{
scriptSigRet.clear();

txntype whichType;
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;

// CHECKMULTISIG bug workaround:
if (vSolutions.size() != 1 ||
vSolutions[0].size() != 1)
CBitcoinAddress address;
valtype subscript;
switch (whichType)
{
scriptSigRet.insert(scriptSigRet.begin(), OP_0);
case TX_NONSTANDARD:
return false;
case TX_PUBKEY:
address.SetPubKey(vSolutions[0]);
return Sign1(address, keystore, hash, nHashType, scriptSigRet);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
if (!Sign1(address, keystore, hash, nHashType, scriptSigRet))
return false;
else
{
valtype vch;
keystore.GetPubKey(address, vch);
scriptSigRet << vch;
}
break;
case TX_SCRIPTHASH:
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
return false;
if (!Solver(keystore, CScript(subscript.begin(), subscript.end()), hash, nHashType, scriptSigRet))
return false;
if (hash != 0)
scriptSigRet << subscript; // signatures AND serialized script
break;
case TX_MULTISIG:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet));
}

return true;
}


bool IsStandard(const CScript& scriptPubKey)
{
vector<vector<pair<opcodetype, valtype> > > vSolutions;
return Solver(scriptPubKey, vSolutions);
vector<valtype> vSolutions;
txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;

if (whichType == TX_MULTISIG)
{
unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0];
// Support up to x-of-3 multisig txns as standard
if (n < 1 || n > 3)
return false;
if (m < 1 || m > n)
return false;
}

return whichType != TX_NONSTANDARD;
}


int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
{
int nResult = 0;
BOOST_FOREACH(const valtype& pubkey, pubkeys)
{
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (keystore.HaveKey(address))
++nResult;
}
return nResult;
}

bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<vector<pair<opcodetype, valtype> > > vSolutions;
if (!Solver(scriptPubKey, vSolutions))
vector<valtype> vSolutions;
txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;

int keysFound = 0;
int keysRequired = 0;
for (int i = 0; i < vSolutions.size(); i++)
CBitcoinAddress address;
switch (whichType)
{
BOOST_FOREACH(PAIRTYPE(opcodetype, valtype)& item, vSolutions[i])
{
++keysRequired;
if (item.first == OP_PUBKEY)
{
const valtype& vchPubKey = item.second;
vector<unsigned char> vchPubKeyFound;
if (keystore.GetPubKey(Hash160(vchPubKey), vchPubKeyFound) && vchPubKeyFound == vchPubKey)
++keysFound;
}
else if (item.first == OP_PUBKEYHASH)
{
if (keystore.HaveKey(uint160(item.second)))
++keysFound;
}
}
case TX_NONSTANDARD:
return false;
case TX_PUBKEY:
address.SetPubKey(vSolutions[0]);
return keystore.HaveKey(address);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
return keystore.HaveKey(address);
case TX_SCRIPTHASH:
{
valtype subscript;
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
return false;
return IsMine(keystore, CScript(subscript.begin(), subscript.end()));
}

// Only consider transactions "mine" if we own ALL the
// keys involved. multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially
// for shared-wallet situations.
return (keysFound == keysRequired);
case TX_MULTISIG:
{
// Only consider transactions "mine" if we own ALL the
// keys involved. multi-signature transactions that are
// partially owned (somebody else has a key that can spend
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
return HaveKeys(vSolutions, keystore);
}
}
return false;
}

bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet)
{
vector<vector<pair<opcodetype, valtype> > > vSolutions;
if (!Solver(scriptPubKey, vSolutions))
vector<valtype> vSolutions;
txntype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;

for (int i = 0; i < vSolutions.size(); i++)
if (whichType == TX_PUBKEY)
{
if (vSolutions[i].size() != 1)
continue; // Can't return more than one address...

PAIRTYPE(opcodetype, valtype)& item = vSolutions[i][0];
if (item.first == OP_PUBKEY)
addressRet.SetPubKey(item.second);
else if (item.first == OP_PUBKEYHASH)
addressRet.SetHash160((uint160)item.second);
if (keystore == NULL || keystore->HaveKey(addressRet))
return true;
addressRet.SetPubKey(vSolutions[0]);
return true;
}
else if (whichType == TX_PUBKEYHASH)
{
addressRet.SetHash160(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
addressRet.SetScriptHash160(uint160(vSolutions[0]));
return true;
}
// Multisig txns have more than one address...
return false;
}

bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txntype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
{
addressRet.clear();
typeRet = TX_NONSTANDARD;
vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions))
return false;

if (typeRet == TX_MULTISIG)
{
nRequiredRet = vSolutions.front()[0];
int n = vSolutions.back()[0];
for (vector<valtype>::const_iterator it = vSolutions.begin()+1; it != vSolutions.begin()+vSolutions.size()-1; it++)
{
CBitcoinAddress address;
address.SetPubKey(*it);
addressRet.push_back(address);
}
}
else
{
nRequiredRet = 1;
CBitcoinAddress address;
if (typeRet == TX_PUBKEYHASH)
address.SetHash160(uint160(vSolutions.front()));
else if (typeRet == TX_SCRIPTHASH)
address.SetScriptHash160(uint160(vSolutions.front()));
else if (typeRet == TX_PUBKEY)
address.SetPubKey(vSolutions.front());
addressRet.push_back(address);
}

bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
return true;
}

bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
{
vector<vector<unsigned char> > stack;
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet))
return false;
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, nSigOpCountRet))
return false;
if (stack.empty())
return false;
return CastToBool(stack.back());
bool fResult = CastToBool(stack.back());

// This code should be removed when a compatibility-breaking block chain split has passed.
// Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains
// OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op:
if (scriptSig.Find(OP_EVAL)+scriptPubKey.Find(OP_EVAL) > 0)
{
int nUnused = 0;
stack.clear();
CScript sigCopy = scriptSig;
sigCopy.FindAndDelete(CScript(OP_EVAL));
CScript pubKeyCopy = scriptPubKey;
pubKeyCopy.FindAndDelete(CScript(OP_EVAL));

if (!EvalScript(stack, sigCopy, txTo, nIn, nHashType, nUnused))
return false;
if (!EvalScript(stack, pubKeyCopy, txTo, nIn, nHashType, nUnused))
return false;
if (stack.empty())
return false;
if (fResult != CastToBool(stack.back()))
return false;
}

return fResult;
}


@@ -1238,15 +1549,16 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
txin.scriptSig = scriptPrereq + txin.scriptSig;

// Test solution
int nUnused = 0;
if (scriptPrereq.empty())
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0))
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0))
return false;

return true;
}


bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType)
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType)
{
assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn];
@@ -1257,27 +1569,35 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
if (txin.prevout.hash != txFrom.GetHash())
return false;

if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType))
return false;

return true;
}

void CScript::SetMultisigAnd(const std::vector<CKey>& keys)
void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
{
assert(keys.size() >= 2);
this->clear();
*this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
if (address.IsScript())
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL;
else
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
}
void CScript::SetMultisigOr(const std::vector<CKey>& keys)

void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
{
assert(keys.size() >= 2);
this->clear();
*this << OP_1 << keys[0].GetPubKey() << keys[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;

*this << EncodeOP_N(nRequired);
BOOST_FOREACH(const CKey& key, keys)
*this << key.GetPubKey();
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
}
void CScript::SetMultisigEscrow(const std::vector<CKey>& keys)

void CScript::SetEval(const CScript& subscript)
{
assert(keys.size() >= 3);
assert(!subscript.empty());
uint160 subscriptHash = Hash160(subscript);
this->clear();
*this << OP_2 << keys[0].GetPubKey() << keys[1].GetPubKey() << keys[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
*this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL;
}

+ 52
- 170
src/script.h View File

@@ -24,6 +24,17 @@ enum
};


enum txntype
{
TX_NONSTANDARD,
// 'standard' transaction types:
TX_PUBKEY,
TX_PUBKEYHASH,
TX_SCRIPTHASH,
TX_MULTISIG,
};

const char* GetTxnTypeName(txntype t);

enum opcodetype
{
@@ -147,8 +158,10 @@ enum opcodetype
OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY,

// meta
OP_EVAL, // Was OP_NOP1

// expansion
OP_NOP1,
OP_NOP2,
OP_NOP3,
OP_NOP4,
@@ -162,162 +175,16 @@ enum opcodetype


// template matching params
OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb,
OP_SCRIPTHASH = 0xfc,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,

OP_INVALIDOPCODE = 0xff,
};








inline const char* GetOpName(opcodetype opcode)
{
switch (opcode)
{
// push value
case OP_0 : return "0";
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
case OP_1NEGATE : return "-1";
case OP_RESERVED : return "OP_RESERVED";
case OP_1 : return "1";
case OP_2 : return "2";
case OP_3 : return "3";
case OP_4 : return "4";
case OP_5 : return "5";
case OP_6 : return "6";
case OP_7 : return "7";
case OP_8 : return "8";
case OP_9 : return "9";
case OP_10 : return "10";
case OP_11 : return "11";
case OP_12 : return "12";
case OP_13 : return "13";
case OP_14 : return "14";
case OP_15 : return "15";
case OP_16 : return "16";

// control
case OP_NOP : return "OP_NOP";
case OP_VER : return "OP_VER";
case OP_IF : return "OP_IF";
case OP_NOTIF : return "OP_NOTIF";
case OP_VERIF : return "OP_VERIF";
case OP_VERNOTIF : return "OP_VERNOTIF";
case OP_ELSE : return "OP_ELSE";
case OP_ENDIF : return "OP_ENDIF";
case OP_VERIFY : return "OP_VERIFY";
case OP_RETURN : return "OP_RETURN";

// stack ops
case OP_TOALTSTACK : return "OP_TOALTSTACK";
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
case OP_2DROP : return "OP_2DROP";
case OP_2DUP : return "OP_2DUP";
case OP_3DUP : return "OP_3DUP";
case OP_2OVER : return "OP_2OVER";
case OP_2ROT : return "OP_2ROT";
case OP_2SWAP : return "OP_2SWAP";
case OP_IFDUP : return "OP_IFDUP";
case OP_DEPTH : return "OP_DEPTH";
case OP_DROP : return "OP_DROP";
case OP_DUP : return "OP_DUP";
case OP_NIP : return "OP_NIP";
case OP_OVER : return "OP_OVER";
case OP_PICK : return "OP_PICK";
case OP_ROLL : return "OP_ROLL";
case OP_ROT : return "OP_ROT";
case OP_SWAP : return "OP_SWAP";
case OP_TUCK : return "OP_TUCK";

// splice ops
case OP_CAT : return "OP_CAT";
case OP_SUBSTR : return "OP_SUBSTR";
case OP_LEFT : return "OP_LEFT";
case OP_RIGHT : return "OP_RIGHT";
case OP_SIZE : return "OP_SIZE";

// bit logic
case OP_INVERT : return "OP_INVERT";
case OP_AND : return "OP_AND";
case OP_OR : return "OP_OR";
case OP_XOR : return "OP_XOR";
case OP_EQUAL : return "OP_EQUAL";
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
case OP_RESERVED1 : return "OP_RESERVED1";
case OP_RESERVED2 : return "OP_RESERVED2";

// numeric
case OP_1ADD : return "OP_1ADD";
case OP_1SUB : return "OP_1SUB";
case OP_2MUL : return "OP_2MUL";
case OP_2DIV : return "OP_2DIV";
case OP_NEGATE : return "OP_NEGATE";
case OP_ABS : return "OP_ABS";
case OP_NOT : return "OP_NOT";
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
case OP_ADD : return "OP_ADD";
case OP_SUB : return "OP_SUB";
case OP_MUL : return "OP_MUL";
case OP_DIV : return "OP_DIV";
case OP_MOD : return "OP_MOD";
case OP_LSHIFT : return "OP_LSHIFT";
case OP_RSHIFT : return "OP_RSHIFT";
case OP_BOOLAND : return "OP_BOOLAND";
case OP_BOOLOR : return "OP_BOOLOR";
case OP_NUMEQUAL : return "OP_NUMEQUAL";
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
case OP_LESSTHAN : return "OP_LESSTHAN";
case OP_GREATERTHAN : return "OP_GREATERTHAN";
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
case OP_MIN : return "OP_MIN";
case OP_MAX : return "OP_MAX";
case OP_WITHIN : return "OP_WITHIN";

// crypto
case OP_RIPEMD160 : return "OP_RIPEMD160";
case OP_SHA1 : return "OP_SHA1";
case OP_SHA256 : return "OP_SHA256";
case OP_HASH160 : return "OP_HASH160";
case OP_HASH256 : return "OP_HASH256";
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
case OP_CHECKSIG : return "OP_CHECKSIG";
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";

// expanson
case OP_NOP1 : return "OP_NOP1";
case OP_NOP2 : return "OP_NOP2";
case OP_NOP3 : return "OP_NOP3";
case OP_NOP4 : return "OP_NOP4";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8";
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";



// template matching params
case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY";

case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default:
return "OP_UNKNOWN";
}
};

const char* GetOpName(opcodetype opcode);



@@ -574,6 +441,7 @@ public:
return true;
}

// Encode/decode small integers:
static int DecodeOP_N(opcodetype opcode)
{
if (opcode == OP_0)
@@ -581,22 +449,44 @@ public:
assert(opcode >= OP_1 && opcode <= OP_16);
return (int)opcode - (int)(OP_1 - 1);
}
static opcodetype EncodeOP_N(int n)
{
assert(n >= 0 && n <= 16);
if (n == 0)
return OP_0;
return (opcodetype)(OP_1+n-1);
}

void FindAndDelete(const CScript& b)
int FindAndDelete(const CScript& b)
{
int nFound = 0;
if (b.empty())
return;
return nFound;
iterator pc = begin();
opcodetype opcode;
do
{
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
{
erase(pc, pc + b.size());
++nFound;
}
}
while (GetOp(pc, opcode));
return nFound;
}
int Find(opcodetype op) const
{
int nFound = 0;
opcodetype opcode;
for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);)
if (opcode == op)
++nFound;
return nFound;
}


// This method should be removed when a compatibility-breaking block chain split has passed.
// Compatibility method for old clients that count sigops differently:
int GetSigOpCount() const
{
int n = 0;
@@ -614,11 +504,9 @@ public:
return n;
}

// Called by CTransaction::IsStandard
bool IsPushOnly() const
{
if (size() > 200)
return false;
const_iterator pc = begin();
while (pc < end())
{
@@ -632,19 +520,13 @@ public:
}


void SetBitcoinAddress(const CBitcoinAddress& address)
{
this->clear();
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
}

void SetBitcoinAddress(const CBitcoinAddress& address);
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
{
SetBitcoinAddress(CBitcoinAddress(vchPubKey));
}
void SetMultisigAnd(const std::vector<CKey>& keys);
void SetMultisigOr(const std::vector<CKey>& keys);
void SetMultisigEscrow(const std::vector<CKey>& keys);
void SetMultisig(int nRequired, const std::vector<CKey>& keys);
void SetEval(const CScript& subscript);


void PrintHex() const
@@ -685,14 +567,14 @@ public:



bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet);


bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);

bool Solver(const CScript& scriptPubKey, txntype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet);
bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txntype& typeRet, std::vector<CBitcoinAddress>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0);
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0);

#endif

+ 52
- 47
src/test/multisig_tests.cpp View File

@@ -20,9 +20,7 @@ using namespace boost::assign;
typedef vector<unsigned char> valtype;

extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool Solver(const CScript& scriptPubKey, vector<vector<pair<opcodetype, valtype> > >& vSolutionsRet);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, int nHashType);

BOOST_AUTO_TEST_SUITE(multisig_tests)

@@ -76,24 +74,25 @@ BOOST_AUTO_TEST_CASE(multisig_verify)

vector<CKey> keys;
CScript s;
int nUnused = 0;

// Test a AND b:
keys.clear();
keys += key[0],key[1]; // magic operator+= from boost.assign
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, 0));
BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0));

for (int i = 0; i < 4; i++)
{
keys.clear();
keys += key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 1: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 1: %d", i));

keys.clear();
keys += key[1],key[i];
s = sign_multisig(a_and_b, keys, txTo[0], 0);
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, 0), strprintf("a&b 2: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 2: %d", i));
}

// Test a OR b:
@@ -103,16 +102,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i];
s = sign_multisig(a_or_b, keys, txTo[1], 0);
if (i == 0 || i == 1)
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, 0), strprintf("a|b: %d", i));
BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i));
}
s.clear();
s << OP_0 << OP_0;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));
s.clear();
s << OP_0 << OP_1;
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, 0));
BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0));


for (int i = 0; i < 4; i++)
@@ -122,16 +121,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify)
keys += key[i],key[j];
s = sign_multisig(escrow, keys, txTo[2], 0);
if (i < j && i < 3 && j < 3)
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 1: %d %d", i, j));
BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 1: %d %d", i, j));
else
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, 0), strprintf("escrow 2: %d %d", i, j));
BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 2: %d %d", i, j));
}
}

BOOST_AUTO_TEST_CASE(multisig_IsStandard)
{
CKey key[3];
for (int i = 0; i < 3; i++)
CKey key[4];
for (int i = 0; i < 4; i++)
key[i].MakeNewKey();

CScript a_and_b;
@@ -145,6 +144,21 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
BOOST_CHECK(::IsStandard(escrow));

CScript one_of_four;
one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG;
BOOST_CHECK(!::IsStandard(one_of_four));

CScript malformed[6];
malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[1] << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_3 << OP_CHECKMULTISIG;
malformed[2] << OP_0 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
malformed[3] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_0 << OP_CHECKMULTISIG;
malformed[4] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_CHECKMULTISIG;
malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey();

for (int i = 0; i < 6; i++)
BOOST_CHECK(!::IsStandard(malformed[i]));
}

BOOST_AUTO_TEST_CASE(multisig_Solver1)
@@ -170,13 +184,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
}

{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << key[0].GetPubKey() << OP_CHECKSIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -184,13 +197,12 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 1);
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));
BOOST_CHECK(addr == keyaddr[0]);
@@ -198,47 +210,40 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(solutions.size() == 1);
if (solutions.size() == 1)
BOOST_CHECK(solutions[0].size() == 2);
BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4);
CBitcoinAddress addr;
BOOST_CHECK(!ExtractAddress(s, &keystore, addr));
BOOST_CHECK(IsMine(keystore, s));
BOOST_CHECK(!IsMine(emptykeystore, s));
}
{
vector<vector<pair<opcodetype, valtype> > > solutions;
vector<valtype> solutions;
txntype whichType;
CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, solutions));
BOOST_CHECK(solutions.size() == 2);
if (solutions.size() == 2)
{
BOOST_CHECK(solutions[0].size() == 1);
BOOST_CHECK(solutions[1].size() == 1);
}
CBitcoinAddress addr;
BOOST_CHECK(ExtractAddress(s, &keystore, addr));