Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisignature and OP_EVAL support #669

Merged
merged 12 commits into from Dec 20, 2011
Merged
@@ -253,31 +253,61 @@ class CBase58Data
};

// base58-encoded bitcoin addresses
// Addresses have version 0 or 111 (testnet)
// Public-key-hash-addresses have version 0 (or 192 testnet)
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key
// Script-hash-addresses (OP_EVAL) have version 5 (or 196 testnet)
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script
class CBitcoinAddress : public CBase58Data
{
public:
void SetHash160(const uint160& hash160)
enum
{
SetData(fTestNet ? 111 : 0, &hash160, 20);
PUBKEY_ADDRESS = 0,
SCRIPT_ADDRESS = 5,
PUBKEY_ADDRESS_TEST = 192,
PUBKEY_ADDRESS_TEST_LEGACY = 111, // Deprecated: old testnet address
SCRIPT_ADDRESS_TEST = 196,
};

bool SetHash160(const uint160& hash160)
{
SetData(fTestNet ? PUBKEY_ADDRESS_TEST : PUBKEY_ADDRESS, &hash160, 20);
return true;
}

void SetPubKey(const std::vector<unsigned char>& vchPubKey)
{
SetHash160(Hash160(vchPubKey));
}

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

bool IsValid() const
{
int nExpectedSize = 20;
bool fExpectTestNet = false;
switch(nVersion)
{
case 0:
case PUBKEY_ADDRESS:
nExpectedSize = 20; // Hash of public key
fExpectTestNet = false;
break;
case SCRIPT_ADDRESS:
nExpectedSize = 20; // OP_EVAL, hash of CScript
fExpectTestNet = false;
break;

case 111:
case PUBKEY_ADDRESS_TEST_LEGACY:
case PUBKEY_ADDRESS_TEST:
nExpectedSize = 20;
fExpectTestNet = true;
break;
case SCRIPT_ADDRESS_TEST:
nExpectedSize = 20;
fExpectTestNet = true;
break;

@@ -286,6 +316,14 @@ class CBitcoinAddress : public CBase58Data
}
return fExpectTestNet == fTestNet && vchData.size() == nExpectedSize;
}
bool IsScript() const
{
if (!IsValid())
return false;
if (fTestNet)
return nVersion == SCRIPT_ADDRESS_TEST;
return nVersion == SCRIPT_ADDRESS;
}

CBitcoinAddress()
{
@@ -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,6 +936,68 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex();
}

Value addmultisigaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "addmultisigaddress <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);
}
if (!fTestNet)
throw runtime_error("addmultisigaddress available only when running -testnet\n");

int nRequired = params[0].get_int();
const Array& keys = params[1].get_array();
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);

// Gather public keys
if (keys.size() < nRequired)
throw runtime_error(
strprintf("addmultisigaddress: wrong number of keys (got %d, need at least %d)", keys.size(), nRequired));
std::vector<CKey> pubkeys;
pubkeys.resize(keys.size());
for (int i = 0; i < keys.size(); i++)
{
const std::string& ks = keys[i].get_str();
if (ks.size() == 130) // hex public key
pubkeys[i].SetPubKey(ParseHex(ks));
else if (ks.size() > 34) // base58-encoded
{
std::vector<unsigned char> vchPubKey;
if (DecodeBase58(ks, vchPubKey))
pubkeys[i].SetPubKey(vchPubKey);
else
throw runtime_error("Error base58 decoding key: "+ks);
}
else // bitcoin address for key in this wallet
{
CBitcoinAddress address(ks);
if (!pwalletMain->GetKey(address, pubkeys[i]))
throw runtime_error(
strprintf("addmultisigaddress: unknown address: %s",ks.c_str()));
}
}

// Construct using OP_EVAL
CScript inner;
inner.SetMultisig(nRequired, pubkeys);

uint160 scriptHash = Hash160(inner);
CScript scriptPubKey;
scriptPubKey.SetEval(inner);
pwalletMain->AddCScript(scriptHash, inner);
CBitcoinAddress address;
address.SetScriptHash160(scriptHash);

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


struct tallyitem
{
@@ -1596,7 +1658,35 @@ Value validateaddress(const Array& params, bool fHelp)
// version of the address:
string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress));
ret.push_back(Pair("ismine", (pwalletMain->HaveKey(address) > 0)));
if (pwalletMain->HaveKey(address))
{
ret.push_back(Pair("ismine", true));
std::vector<unsigned char> vchPubKey;
pwalletMain->GetPubKey(address, vchPubKey);
ret.push_back(Pair("pubkey", HexStr(vchPubKey)));
std::string strPubKey(vchPubKey.begin(), vchPubKey.end());
ret.push_back(Pair("pubkey58", EncodeBase58(vchPubKey)));
}
else if (pwalletMain->HaveCScript(address.GetHash160()))
{
ret.push_back(Pair("isscript", true));
CScript subscript;
pwalletMain->GetCScript(address.GetHash160(), subscript);
ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript)));
std::vector<CBitcoinAddress> addresses;
txnouttype whichType;
int nRequired;
ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired);
ret.push_back(Pair("script", GetTxnOutputType(whichType)));
Array a;
BOOST_FOREACH(const CBitcoinAddress& addr, addresses)
a.push_back(addr.ToString());
ret.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
ret.push_back(Pair("sigsrequired", nRequired));
}
else
ret.push_back(Pair("ismine", false));
if (pwalletMain->mapAddressBook.count(address))
ret.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
}
@@ -1841,6 +1931,7 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("move", &movecmd),
make_pair("sendfrom", &sendfrom),
make_pair("sendmany", &sendmany),
make_pair("addmultisigaddress", &addmultisigaddress),
make_pair("gettransaction", &gettransaction),
make_pair("listtransactions", &listtransactions),
make_pair("signmessage", &signmessage),
@@ -2484,6 +2575,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 == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1)
{
string s = params[1].get_str();
Value v;
if (!read_string(s, v) || v.type() != array_type)
throw runtime_error("addmultisigaddress: type mismatch "+s);
params[1] = v.get_array();
}

// Execute
Object reply = CallRPC(strMethod, params);
@@ -4,6 +4,7 @@
#ifndef __CRYPTER_H__
#define __CRYPTER_H__

#include "util.h" /* for SecureString */
#include "key.h"

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

#include <db_cxx.h>

class CTxIndex;
class CAccount;
class CAccountingEntry;
class CAddress;
class CBlockLocator;
class CDiskBlockIndex;
class CDiskTxPos;
class CMasterKey;
class COutPoint;
class CAddress;
class CWalletTx;
class CTxIndex;
class CWallet;
class CAccount;
class CAccountingEntry;
class CBlockLocator;

class CWalletTx;

extern unsigned int nWalletDBUpdated;
extern DbEnv dbenv;
@@ -420,6 +420,19 @@ class CWalletDB : public CDB
return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true);
}

// Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013
bool ReadCScript(const uint160 &hash, CScript& redeemScript)
{
redeemScript.clear();
return Read(std::make_pair(std::string("cscript"), hash), redeemScript);
}

bool WriteCScript(const uint160& hash, const CScript& redeemScript)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}

bool WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
@@ -4,8 +4,9 @@
// file license.txt or http://www.opensource.org/licenses/mit-license.php.

#include "headers.h"
#include "db.h"
#include "crypter.h"
#include "db.h"
#include "script.h"

std::vector<unsigned char> CKeyStore::GenerateNewKey()
{
@@ -33,6 +34,36 @@ bool CBasicKeyStore::AddKey(const CKey& key)
return true;
}

bool CBasicKeyStore::AddCScript(const uint160 &hash, const CScript& redeemScript)
{
CRITICAL_BLOCK(cs_KeyStore)
mapScripts[hash] = redeemScript;
return true;
}

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


bool CBasicKeyStore::GetCScript(const uint160 &hash, CScript& redeemScriptOut) const
{
CRITICAL_BLOCK(cs_KeyStore)
{
ScriptMap::const_iterator mi = mapScripts.find(hash);
if (mi != mapScripts.end())
{
redeemScriptOut = (*mi).second;
return true;
}
}
return false;
}

bool CCryptoKeyStore::SetCrypted()
{
CRITICAL_BLOCK(cs_KeyStore)
@@ -6,6 +6,7 @@
#define BITCOIN_KEYSTORE_H

#include "crypter.h"
#include "script.h"

// A virtual base class for key stores
class CKeyStore
@@ -31,6 +32,11 @@ class CKeyStore
virtual void GetKeys(std::set<CBitcoinAddress> &setAddress) const =0;
virtual bool GetPubKey(const CBitcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;

// Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013
virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript) =0;
virtual bool HaveCScript(const uint160 &hash) const =0;
virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const =0;

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

typedef std::map<CBitcoinAddress, CSecret> KeyMap;
typedef std::map<uint160, CScript > ScriptMap;

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

public:
bool AddKey(const CKey& key);
@@ -86,6 +94,9 @@ class CBasicKeyStore : public CKeyStore
}
return false;
}
virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript);
virtual bool HaveCScript(const uint160 &hash) const;
virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const;
};

typedef std::map<CBitcoinAddress, std::pair<std::vector<unsigned char>, std::vector<unsigned char> > > CryptedKeyMap;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.