Refactor: split CKeyID/CScriptID/CTxDestination from CBitcoinAddress

This introduces internal types:
* CKeyID: reference (hash160) of a key
* CScriptID: reference (hash160) of a script
* CTxDestination: a boost::variant of the former two

CBitcoinAddress is retrofitted to be a Base58 encoding of a
CTxDestination. This allows all internal code to only use the
internal types, and only have RPC and GUI depend on the base58 code.

Furthermore, the header dependencies are a lot saner now. base58.h is
at the top (right below rpc and gui) instead of at the bottom. For the
rest: wallet -> script -> keystore -> key. Only keystore still requires
a forward declaration of CScript. Solving that would require splitting
script into two layers.
This commit is contained in:
Pieter Wuille
2012-05-14 23:44:52 +02:00
parent fd61d6f506
commit 1025440184
26 changed files with 477 additions and 339 deletions

View File

@@ -1312,7 +1312,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
}
bool Sign1(const CBitcoinAddress& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
bool Sign1(const CKeyID& address, const CKeyStore& keystore, uint256 hash, int nHashType, CScript& scriptSigRet)
{
CKey key;
if (!keystore.GetKey(address, key))
@@ -1334,9 +1334,8 @@ bool SignN(const vector<valtype>& multisigdata, const CKeyStore& keystore, uint2
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))
CKeyID keyID = CPubKey(pubkey).GetID();
if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet))
{
++nSigned;
if (nSigned == nRequired) break;
@@ -1360,22 +1359,22 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
if (!Solver(scriptPubKey, whichTypeRet, vSolutions))
return false;
CBitcoinAddress address;
CKeyID keyID;
switch (whichTypeRet)
{
case TX_NONSTANDARD:
return false;
case TX_PUBKEY:
address.SetPubKey(vSolutions[0]);
return Sign1(address, keystore, hash, nHashType, scriptSigRet);
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, keystore, hash, nHashType, scriptSigRet);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
if (!Sign1(address, keystore, hash, nHashType, scriptSigRet))
keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, keystore, hash, nHashType, scriptSigRet))
return false;
else
{
CPubKey vch;
keystore.GetPubKey(address, vch);
keystore.GetPubKey(keyID, vch);
scriptSigRet << vch;
}
return true;
@@ -1436,14 +1435,30 @@ unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
unsigned int nResult = 0;
BOOST_FOREACH(const valtype& pubkey, pubkeys)
{
CBitcoinAddress address;
address.SetPubKey(pubkey);
if (keystore.HaveKey(address))
CKeyID keyID = CPubKey(pubkey).GetID();
if (keystore.HaveKey(keyID))
++nResult;
}
return nResult;
}
class CKeyStoreIsMineVisitor : public boost::static_visitor<bool>
{
private:
const CKeyStore *keystore;
public:
CKeyStoreIsMineVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { }
bool operator()(const CNoDestination &dest) const { return false; }
bool operator()(const CKeyID &keyID) const { return keystore->HaveKey(keyID); }
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
};
bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
{
return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest);
}
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
@@ -1451,21 +1466,21 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
CBitcoinAddress address;
CKeyID keyID;
switch (whichType)
{
case TX_NONSTANDARD:
return false;
case TX_PUBKEY:
address.SetPubKey(vSolutions[0]);
return keystore.HaveKey(address);
keyID = CPubKey(vSolutions[0]).GetID();
return keystore.HaveKey(keyID);
case TX_PUBKEYHASH:
address.SetHash160(uint160(vSolutions[0]));
return keystore.HaveKey(address);
keyID = CKeyID(uint160(vSolutions[0]));
return keystore.HaveKey(keyID);
case TX_SCRIPTHASH:
{
CScript subscript;
if (!keystore.GetCScript(uint160(vSolutions[0]), subscript))
if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
return false;
return IsMine(keystore, subscript);
}
@@ -1483,7 +1498,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
return false;
}
bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet)
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
vector<valtype> vSolutions;
txnouttype whichType;
@@ -1492,24 +1507,24 @@ bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet)
if (whichType == TX_PUBKEY)
{
addressRet.SetPubKey(vSolutions[0]);
addressRet = CPubKey(vSolutions[0]).GetID();
return true;
}
else if (whichType == TX_PUBKEYHASH)
{
addressRet.SetHash160(uint160(vSolutions[0]));
addressRet = CKeyID(uint160(vSolutions[0]));
return true;
}
else if (whichType == TX_SCRIPTHASH)
{
addressRet.SetScriptHash160(uint160(vSolutions[0]));
addressRet = CScriptID(uint160(vSolutions[0]));
return true;
}
// Multisig txns have more than one address...
return false;
}
bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, vector<CBitcoinAddress>& addressRet, int& nRequiredRet)
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vector<CTxDestination>& addressRet, int& nRequiredRet)
{
addressRet.clear();
typeRet = TX_NONSTANDARD;
@@ -1522,21 +1537,16 @@ bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, vector<C
nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
{
CBitcoinAddress address;
address.SetPubKey(vSolutions[i]);
CTxDestination address = CPubKey(vSolutions[i]).GetID();
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());
CTxDestination address;
if (!ExtractDestination(scriptPubKey, address))
return false;
addressRet.push_back(address);
}
@@ -1694,13 +1704,34 @@ bool CScript::IsPayToScriptHash() const
this->at(22) == OP_EQUAL);
}
void CScript::SetBitcoinAddress(const CBitcoinAddress& address)
class CScriptVisitor : public boost::static_visitor<bool>
{
this->clear();
if (address.IsScript())
*this << OP_HASH160 << address.GetHash160() << OP_EQUAL;
else
*this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG;
private:
CScript *script;
public:
CScriptVisitor(CScript *scriptin) { script = scriptin; }
bool operator()(const CNoDestination &dest) const {
script->clear();
return false;
}
bool operator()(const CKeyID &keyID) const {
script->clear();
*script << OP_DUP << OP_HASH160 << keyID << OP_EQUALVERIFY << OP_CHECKSIG;
return true;
}
bool operator()(const CScriptID &scriptID) const {
script->clear();
*script << OP_HASH160 << scriptID << OP_EQUAL;
return true;
}
};
void CScript::SetDestination(const CTxDestination& dest)
{
boost::apply_visitor(CScriptVisitor(this), dest);
}
void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
@@ -1712,11 +1743,3 @@ void CScript::SetMultisig(int nRequired, const std::vector<CKey>& keys)
*this << key.GetPubKey();
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
}
void CScript::SetPayToScriptHash(const CScript& subscript)
{
assert(!subscript.empty());
uint160 subscriptHash = Hash160(subscript);
this->clear();
*this << OP_HASH160 << subscriptHash << OP_EQUAL;
}