New wallet system. Still missing unit tests, ledger synchronization,

and SQL code. This uses some of the 'magic' properties of elliptic
curves to create related families of public and private keys.
Wallet encryption is not needed because the private keys do not
need to be stored.
This commit is contained in:
JoelKatz
2011-12-26 18:42:50 -08:00
parent 780f94798e
commit 5cec2e9a11
11 changed files with 477 additions and 242 deletions

View File

@@ -1,185 +1,224 @@
#include <string>
#include "boost/lexical_cast.hpp"
#include "Wallet.h"
#include "NewcoinAddress.h"
#include "Application.h"
#include <string>
#include <boost/foreach.hpp>
LocalAccount::LocalAccount(bool) : mPublicKey(new CKey()), mAmount(0), mSeqNum(0)
LocalAccountEntry::LocalAccountEntry(const uint160& accountFamily, int accountSeq, EC_POINT* rootPubKey) :
mAccountFamily(accountFamily), mAccountSeq(accountSeq),
mPublicKey(new CKey(accountFamily, rootPubKey, accountSeq)),
mBalance(0), mLedgerSeq(0), mTxnSeq(0)
{
mPrivateKey.MakeNewKey();
mPublicKey->SetPubKey(mPrivateKey.GetPubKey());
acctID=Hash160(mPublicKey->GetPubKey());
mPublicKey=theApp->getPubKeyCache().store(acctID, mPublicKey);
assert(mPublicKey);
mAddress.SetHash160(acctID);
mAcctID=mPublicKey->GetAddress().GetHash160();
mPublicKey=theApp->getPubKeyCache().store(mAcctID, mPublicKey);
}
Wallet::Wallet()
void LocalAccountEntry::unlock(BIGNUM* rootPrivKey)
{
if((mPrivateKey==NULL) && (rootPrivKey!=NULL))
mPrivateKey=CKey::pointer(new CKey(mAccountFamily, rootPrivKey, mAccountSeq));
}
std::string LocalAccountEntry::getAccountName() const
{
return mPublicKey->GetAddress().GetString();
}
std::string LocalAccountEntry::getLocalAccountName() const
{
return NewcoinAddress(mAccountFamily).GetString() + ":" + boost::lexical_cast<std::string>(mAccountSeq);
}
LocalAccountFamily::LocalAccountFamily(const uint160& family, EC_POINT* pubKey) :
mFamily(family), rootPubKey(pubKey), rootPrivateKey(NULL)
{ ; }
LocalAccountFamily::~LocalAccountFamily()
{
lock();
if(rootPubKey!=NULL) EC_POINT_free(rootPubKey);
}
uint160 LocalAccountFamily::getAccount(int seq)
{
std::map<int, LocalAccountEntry::pointer>::iterator ait=mAccounts.find(seq);
if(ait!=mAccounts.end()) return ait->second->getAccountID();
LocalAccountEntry::pointer lae(new LocalAccountEntry(mFamily, seq, rootPubKey));
mAccounts.insert(std::make_pair(seq, lae));
return lae->getAccountID();
}
void LocalAccountFamily::unlock(BIGNUM* privateKey)
{
if(rootPrivateKey!=NULL) BN_free(rootPrivateKey);
rootPrivateKey=privateKey;
}
void LocalAccountFamily::lock()
{
if(rootPrivateKey!=NULL)
{
BN_free(rootPrivateKey);
rootPrivateKey=NULL;
for(std::map<int, LocalAccountEntry::pointer>::iterator it=mAccounts.begin(); it!=mAccounts.end(); ++it)
it->second->lock();
}
}
LocalAccountEntry::pointer LocalAccountFamily::get(int seq)
{
std::map<int, LocalAccountEntry::pointer>::iterator act=mAccounts.find(seq);
if(act!=mAccounts.end()) return act->second;
LocalAccountEntry::pointer ret(new LocalAccountEntry(mFamily, seq, rootPubKey));
mAccounts.insert(std::make_pair(seq, ret));
return ret;
}
uint160 Wallet::addFamily(const std::string& payPhrase, bool lock)
{
return doPrivate(CKey::PassPhraseToKey(payPhrase), true, !lock);
}
bool Wallet::addFamily(const uint160& familyName, const std::string& pubKey)
{
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=families.find(familyName);
if(fit!=families.end()) // already added
return true;
EC_GROUP *grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
if(!grp) return false;
BIGNUM* pbn=NULL;
BN_hex2bn(&pbn, pubKey.c_str());
if(!pbn)
{
EC_GROUP_free(grp);
return false;
}
EC_POINT* rootPub=EC_POINT_bn2point(grp, pbn, NULL, NULL);
EC_GROUP_free(grp);
BN_free(pbn);
if(!rootPub)
{
assert(false);
return false;
}
LocalAccountFamily::pointer fam(new LocalAccountFamily(familyName, rootPub));
families.insert(std::make_pair(familyName, fam));
return true;
}
std::string Wallet::getPubKey(const uint160& famBase)
{
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=families.find(famBase);
if(fit==families.end()) return "";
EC_GROUP *grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
if(!grp) return "";
BIGNUM* pubBase=EC_POINT_point2bn(grp, fit->second->peekPubKey(), POINT_CONVERSION_COMPRESSED, NULL, NULL);
EC_GROUP_free(grp);
if(!pubBase) return "";
char *hex=BN_bn2hex(pubBase);
BN_free(pubBase);
if(!hex) return "";
std::string ret(hex);
OPENSSL_free(hex);
return ret;
}
uint160 LocalAccount::getAddress() const
{
LocalAccountEntry::pointer la(mFamily->get(mSeq));
if(!la) return uint160();
return la->getAccountID();
}
uint32 LocalAccount::getAcctSeq() const
{
LocalAccountEntry::pointer la(mFamily->get(mSeq));
if(!la) return 0;
return la->getAccountSeq();
}
uint64 LocalAccount::getBalance() const
{
LocalAccountEntry::pointer la(mFamily->get(mSeq));
if(!la) return 0;
return la->getBalance();
}
CKey::pointer LocalAccount::getPublicKey()
{
LocalAccountEntry::pointer la(mFamily->get(mSeq));
if(!la) return CKey::pointer();
return la->getPubKey();
}
CKey::pointer LocalAccount::getPrivateKey()
{
LocalAccountEntry::pointer la(mFamily->get(mSeq));
if(!la) return CKey::pointer();
return la->getPrivKey();
}
void Wallet::load()
{
// WRITEME
}
#if 0
int64 Wallet::getBalance()
LocalAccount::pointer Wallet::getLocalAccount(const uint160& family, int seq)
{
int64 total = 0;
LedgerMaster& ledgerMaster=theApp->getLedgerMaster();
BOOST_FOREACH(Account& account, mYourAccounts)
{
total += ledgerMaster.getAmountHeld(account.mAddress);
}
return total;
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=families.find(family);
if(fit==families.end()) return LocalAccount::pointer();
uint160 acct=fit->second->getAccount(seq);
std::map<uint160, LocalAccount::pointer>::iterator ait=accounts.find(acct);
if(ait!=accounts.end()) return ait->second;
LocalAccount::pointer lac(new LocalAccount(fit->second, seq));
accounts.insert(std::make_pair(acct, lac));
return lac;
}
void Wallet::refreshAccounts()
uint160 Wallet::doPrivate(const uint256& passPhrase, bool create, bool unlock)
{
LedgerMaster& ledgerMaster=theApp->getLedgerMaster();
// Generate root key
EC_KEY *base=CKey::GenerateRootDeterministicKey(passPhrase);
BOOST_FOREACH(Account& account, mYourAccounts)
{
Ledger::Account* ledgerAccount=ledgerMaster.getAccount(account.mAddress);
if(ledgerAccount)
// Extract family name
std::vector<unsigned char> rootPubKey(33, 0);
unsigned char *begin=&rootPubKey[0];
i2o_ECPublicKey(base, &begin);
while(rootPubKey.size()<33) rootPubKey.push_back((unsigned char)0);
uint160 family=NewcoinAddress(rootPubKey).GetHash160();
LocalAccountFamily::pointer fam;
std::map<uint160, LocalAccountFamily::pointer>::iterator it=families.find(family);
if(it==families.end())
{ // family not found
if(!create)
{
account.mAmount= ledgerAccount->first;
account.mSeqNum= ledgerAccount->second;
}else
{
account.mAmount=0;
account.mSeqNum=0;
EC_KEY_free(base);
return family;
}
fam=LocalAccountFamily::pointer(new LocalAccountFamily(family,
EC_POINT_dup(EC_KEY_get0_public_key(base), EC_KEY_get0_group(base))));
families[family]=fam;
}
else fam=it->second;
if(unlock && fam->isLocked())
fam->unlock(BN_dup(EC_KEY_get0_private_key(base)));
EC_KEY_free(base);
return family;
}
void Wallet::transactionChanged(TransactionPtr trans)
{
BOOST_FOREACH(Account& account, mYourAccounts)
{
if( account.mAddress == protobufTo160(trans->from()) ||
account.mAddress == protobufTo160(trans->dest()) )
{
Ledger::Account* ledgerAccount=theApp->getLedgerMaster().getAccount(account.mAddress);
if(ledgerAccount)
{
account.mAmount= ledgerAccount->first;
account.mSeqNum= ledgerAccount->second;
}else
{
account.mAmount=0;
account.mSeqNum=0;
}
}
}
}
Wallet::Account* Wallet::consolidateAccountOfSize(int64 amount)
{
int64 total=0;
BOOST_FOREACH(Account& account, mYourAccounts)
{
if(account.mAmount>=amount) return(&account);
total += account.mAmount;
}
if(total<amount) return(NULL);
Account* firstAccount=NULL;
uint160* firstAddr=NULL;
total=0;
BOOST_FOREACH(Account& account, mYourAccounts)
{
total += account.mAmount;
if(firstAccount)
{
TransactionPtr trans=createTransaction(account,firstAccount->mAddress,account.mAmount);
commitTransaction(trans);
}else firstAccount=&account;
if(total>=amount) return(firstAccount);
}
assert(0);
return(NULL);
}
string Wallet::sendMoneyToAddress(uint160& destAddress, int64 amount)
{
// we may have to bundle up money in order to send this amount
Account* fromAccount=consolidateAccountOfSize(amount);
if(fromAccount)
{
TransactionPtr trans=createTransaction(*fromAccount,destAddress,amount);
if(trans)
{
if(!commitTransaction(trans))
return("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
}
}else return("Insufficient funds");
return "";
}
TransactionPtr Wallet::createTransaction(Account& fromAccount, uint160& destAddr, int64 amount)
{
TransactionPtr trans(new newcoin::Transaction());
trans->set_amount(amount);
trans->set_seqnum(fromAccount.mSeqNum);
trans->set_from(fromAccount.mAddress.begin(), fromAccount.mAddress.GetSerializeSize());
trans->set_dest(destAddr.begin(),destAddr.GetSerializeSize());
trans->set_ledgerindex(theApp->getLedgerMaster().getCurrentLedgerIndex());
// TODO: trans->set_pubkey(fromAccount.mPublicKey);
fromAccount.signTransaction(trans);
return(trans);
}
bool Wallet::Account::signTransaction(TransactionPtr trans)
{
/* TODO:
uint256 hash = Transaction::calcHash(trans);
CKey key;
if(!GetKey(input.from(), key))
return false;
if(hash != 0)
{
vector<unsigned char> vchSig;
if(!key.Sign(hash, retSig))
return false;
}
*/
return(true);
}
bool Wallet::commitTransaction(TransactionPtr trans)
{
if(trans)
{
if(theApp->getLedgerMaster().addTransaction(trans))
{
ConnectionPool& pool=theApp->getConnectionPool();
PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(new newcoin::Transaction(*(trans.get()))),newcoin::TRANSACTION));
pool.relayMessage(NULL,packet);
}else cout << "Problem adding the transaction to your local ledger" << endl;
}
return(false);
}
#endif