mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
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:
359
Wallet.cpp
359
Wallet.cpp
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user