From 5cec2e9a11620ddf90d167e7b6cfb2e636ffad5e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 26 Dec 2011 18:42:50 -0800 Subject: [PATCH] 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. --- Application.cpp | 9 +- DeterministicKeys.cpp | 106 +++++++++++-- Ledger.cpp | 13 +- NewcoinAddress.cpp | 7 +- NewcoinAddress.h | 2 +- SQLiteDatabases.sql | 17 +- Transaction.cpp | 18 ++- Transaction.h | 8 +- Wallet.cpp | 359 +++++++++++++++++++++++------------------- Wallet.h | 156 +++++++++++++----- key.h | 24 ++- 11 files changed, 477 insertions(+), 242 deletions(-) diff --git a/Application.cpp b/Application.cpp index cd062455f1..360a3c3425 100644 --- a/Application.cpp +++ b/Application.cpp @@ -32,9 +32,14 @@ Application::Application() mRPCDoor=NULL; mDatabase=NULL; - CKey::pointer account_key(new CKey(CKey::GetBaseFromString("This is my payphrase."), 0, false)); - Ledger::pointer firstLedger(new Ledger(account_key->GetAddress().GetHash160(), 1000000)); + uint160 rootFamily=mWallet.addFamily("This is my payphrase.", true); + LocalAccount::pointer rootAccount=mWallet.getLocalAccount(rootFamily, 0); + assert(!!rootAccount); + uint160 rootAddress=rootAccount->getAddress(); + assert(!!rootAddress); + + Ledger::pointer firstLedger(new Ledger(rootAddress, 1000000)); firstLedger->setClosed(); firstLedger->setAccepted(); mMasterLedger.pushLedger(firstLedger); diff --git a/DeterministicKeys.cpp b/DeterministicKeys.cpp index 5c417c1845..24e9dc5b5c 100644 --- a/DeterministicKeys.cpp +++ b/DeterministicKeys.cpp @@ -8,16 +8,16 @@ #include "Serializer.h" -uint256 CKey::GetBaseFromString(const std::string& phrase) +uint256 CKey::PassPhraseToKey(const std::string& passPhrase) { Serializer s; - s.addRaw((const void *) phrase.c_str(), phrase.length()); - uint256 base(s.getSHA512Half()); + s.addRaw(passPhrase.c_str(), passPhrase.size()); + uint256 ret(s.getSHA512Half()); s.secureErase(); - return base; + return ret; } -EC_KEY* CKey::GenerateDeterministicKey(const uint256& base, uint32 n, bool private_key) +EC_KEY* CKey::GenerateRootDeterministicKey(const uint256& key) { BN_CTX* ctx=BN_CTX_new(); if(!ctx) return NULL; @@ -44,9 +44,8 @@ EC_KEY* CKey::GenerateDeterministicKey(const uint256& base, uint32 n, bool priva int seq=0; do { // private key must be non-zero and less than the curve's order - Serializer s; - s.add32(n); - s.add256(base); + Serializer s(72); + s.add256(key); s.add32(seq++); uint256 root=s.getSHA512Half(); s.secureErase(); @@ -62,7 +61,7 @@ EC_KEY* CKey::GenerateDeterministicKey(const uint256& base, uint32 n, bool priva BN_free(order); - if(private_key && !EC_KEY_set_private_key(pkey, privKey)) + if(!EC_KEY_set_private_key(pkey, privKey)) { // set the random point as the private key assert(false); EC_KEY_free(pkey); @@ -98,8 +97,91 @@ EC_KEY* CKey::GenerateDeterministicKey(const uint256& base, uint32 n, bool priva return pkey; } -uint256 CKey::GetRandomBase(void) +static BIGNUM* makeHash(const uint160& family, int seq) { - uint256 r; - return (RAND_bytes((unsigned char *) &r, sizeof(uint256)) == 1) ? r : 0; + Serializer s; + s.add160(family); + s.add32(seq); + uint256 root=s.getSHA512Half(); + s.secureErase(); + return BN_bin2bn((const unsigned char *) &root, sizeof(root), NULL); +} + +EC_KEY* CKey::GeneratePublicDeterministicKey(const uint160& family, EC_POINT* rootPubKey, int seq) +{ // publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point + BN_CTX* ctx=BN_CTX_new(); + if(ctx==NULL) return NULL; + + EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1); + if(pkey==NULL) + { + BN_CTX_free(ctx); + return NULL; + } + + EC_POINT *newPoint=EC_POINT_new(EC_KEY_get0_group(pkey)); + if(newPoint==NULL) + { + EC_KEY_free(pkey); + BN_CTX_free(ctx); + return NULL; + } + + BIGNUM* hash=makeHash(family, seq); + if(hash==NULL) + { + EC_POINT_free(newPoint); + BN_CTX_free(ctx); + EC_KEY_free(pkey); + return NULL; + } + + EC_POINT_mul(EC_KEY_get0_group(pkey), newPoint, hash, NULL, NULL, ctx); + BN_free(hash); + + EC_POINT_add(EC_KEY_get0_group(pkey), newPoint, newPoint, rootPubKey, ctx); + EC_KEY_set_public_key(pkey, newPoint); + + EC_POINT_free(newPoint); + BN_CTX_free(ctx); + + return pkey; +} + +EC_KEY* CKey::GeneratePrivateDeterministicKey(const uint160& family, BIGNUM* rootPrivKey, int seq) +{ // privateKey(n) = (rootPrivateKey + Hash(pubHash|seq)) % order + BN_CTX* ctx=BN_CTX_new(); + if(ctx==NULL) return NULL; + + EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1); + if(pkey==NULL) + { + BN_CTX_free(ctx); + return NULL; + } + + BIGNUM* order=BN_new(); + if(order==NULL) + { + BN_CTX_free(ctx); + EC_KEY_free(pkey); + } + + EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx); + + BIGNUM* privKey=makeHash(family, seq); + BN_mod_add(privKey, privKey, rootPrivKey, order, ctx); + BN_free(order); + + EC_KEY_set_private_key(pkey, privKey); + + EC_POINT* pubKey=EC_POINT_new(EC_KEY_get0_group(pkey)); + EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx); + BN_free(privKey); + EC_KEY_set_public_key(pkey, pubKey); + + EC_POINT_free(pubKey); + BN_CTX_free(ctx); + + return pkey; } diff --git a/Ledger.cpp b/Ledger.cpp index b3439188d7..54e77f2082 100644 --- a/Ledger.cpp +++ b/Ledger.cpp @@ -260,17 +260,20 @@ Ledger::pointer Ledger::closeLedger(uint64 timeStamp) bool Ledger::unitTest() { - LocalAccount l1(true), l2(true); - assert(l1.peekPubKey()); + uint160 la1=theApp->getWallet().addFamily(CKey::PassPhraseToKey("This is my payphrase!"), false); + uint160 la2=theApp->getWallet().addFamily(CKey::PassPhraseToKey("Another payphrase"), false); + + LocalAccount::pointer l1=theApp->getWallet().getLocalAccount(la1, 0); + LocalAccount::pointer l2=theApp->getWallet().getLocalAccount(la2, 0); + + assert(l1->getAddress()==la1); - uint160 la1(l1.getAddress()), la2(l2.getAddress()); #ifdef DEBUG std::cerr << "Account1: " << la1.GetHex() << std::endl; std::cerr << "Account2: " << la2.GetHex() << std::endl; #endif Ledger::pointer ledger(new Ledger(la1, 100000)); - l1.mAmount=100000; ledger=Ledger::pointer(new Ledger(*ledger, 0)); @@ -281,7 +284,7 @@ bool Ledger::unitTest() as=ledger->getAccountState(la2); assert(!as); - Transaction::pointer t(new Transaction(NEW, l1, l1.mSeqNum, l2.getAddress(), 2500, 0, 1)); + Transaction::pointer t(new Transaction(NEW, l1, l1->getAcctSeq(), l2->getAddress(), 2500, 0, 1)); assert(!!t->getID()); Ledger::TransResult tr=ledger->applyTransaction(t); diff --git a/NewcoinAddress.cpp b/NewcoinAddress.cpp index 816985a014..01b2036488 100644 --- a/NewcoinAddress.cpp +++ b/NewcoinAddress.cpp @@ -4,7 +4,6 @@ #include - bool NewcoinAddress::SetHash160(const uint160& hash160) { SetData(51, &hash160, 20); @@ -25,7 +24,7 @@ NewcoinAddress::NewcoinAddress() { } -NewcoinAddress::NewcoinAddress(uint160& hash160In) +NewcoinAddress::NewcoinAddress(const uint160& hash160In) { SetHash160(hash160In); } @@ -53,3 +52,7 @@ uint160 NewcoinAddress::GetHash160() const return hash160; } +std::string NewcoinAddress::GetString() const +{ + return ToString(); +} diff --git a/NewcoinAddress.h b/NewcoinAddress.h index 59a8881f9b..9b1a299a19 100644 --- a/NewcoinAddress.h +++ b/NewcoinAddress.h @@ -11,7 +11,7 @@ class NewcoinAddress : public CBase58Data { public: NewcoinAddress(); - NewcoinAddress(uint160& hash160In); + NewcoinAddress(const uint160& hash160In); NewcoinAddress(const std::vector& vchPubKey); NewcoinAddress(const std::string& strAddress); NewcoinAddress(const char* pszAddress); diff --git a/SQLiteDatabases.sql b/SQLiteDatabases.sql index 57d47e1374..6a4d337914 100644 --- a/SQLiteDatabases.sql +++ b/SQLiteDatabases.sql @@ -35,7 +35,6 @@ CREATE TABLE Ledgers ( -- closed/accepted ledgers CREATE INDEX SeqLedger ON Ledgers(LedgerSeq); - CREATE TABLE LedgerConfirmations ( LedgerSeq BIGINT UNSIGNED, LedgerHash CHARACTER(64), @@ -69,12 +68,22 @@ CREATE TABLE CommittedObjects ( -- used to synch nodes CREATE INDEX ObjectLocate ON CommittedObjects(LedgerIndex, ObjType); -CREATE TABLE LocalAccounts ( -- wallet +CREATE TABLE LocalAcctFamilies ( -- a family of accounts that share a payphrase + FamilyName CHARACTER(40) PRIMARY KEY, + RootPubKey CHARACTER(66), + Seq BIGINT UNSIGNED, -- last one issued + Name TEXT, + Comment TEXT +) + +CREATE TABLE LocalAccounts ( -- an individual account ID CHARACTER(40) PRIMARY KEY, + DKID CHARACTER(40), -- root determinstic key + DKSeq BIGINT UNSIGNED, -- sequence number Seq BIGINT UNSIGNED, -- last transaction seen/issued Balance BIGINT UNSIGNED, LedgerSeq BIGINT UNSIGNED, -- ledger this balance is from - KeyFormat CHARACTER(1), -- can be encrypted - PrivateKey BLOB, Comment TEXT ); + +CREATE UNIQUE INDEX AccountLocate ON LocalAccounts(DKID, DKSeq); diff --git a/Transaction.cpp b/Transaction.cpp index 2c58cec1a4..1cb7a4febe 100644 --- a/Transaction.cpp +++ b/Transaction.cpp @@ -14,14 +14,13 @@ Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0), { } -Transaction::Transaction(TransStatus status, LocalAccount& fromLocalAccount, uint32 fromSeq, +Transaction::Transaction(TransStatus status, LocalAccount::pointer fromLocalAccount, uint32 fromSeq, const uint160& toAccount, uint64 amount, uint32 ident, uint32 ledger) : mAccountTo(toAccount), mAmount(amount), mFromAccountSeq(fromSeq), mSourceLedger(ledger), mIdent(ident), mInLedger(0), mStatus(NEW) { - assert(fromLocalAccount.mAmount>=amount); - mAccountFrom=fromLocalAccount.getAddress(); - mFromPubKey=fromLocalAccount.peekPubKey(); + mAccountFrom=fromLocalAccount->getAddress(); + mFromPubKey=fromLocalAccount->getPublicKey(); assert(mFromPubKey); updateFee(); sign(fromLocalAccount); @@ -51,24 +50,27 @@ Transaction::Transaction(const std::vector &t, bool validate) : m mStatus=NEW; } -bool Transaction::sign(LocalAccount& fromLocalAccount) +bool Transaction::sign(LocalAccount::pointer fromLocalAccount) { + CKey::pointer privateKey=fromLocalAccount->getPrivateKey(); + if(!privateKey) return false; + if( (mAmount==0) || (mSourceLedger==0) || (mAccountTo==0) ) { assert(false); return false; } - if(mAccountFrom!=fromLocalAccount.mAddress.GetHash160()) + if(mAccountFrom!=fromLocalAccount->getAddress()) { assert(false); return false; } Serializer::pointer signBuf=getRaw(true); assert(signBuf->getLength()==73+4); - if(!signBuf->makeSignature(mSignature, fromLocalAccount.peekPrivKey())) + if(!signBuf->makeSignature(mSignature, *privateKey)) { assert(false); - return false; + return false; } assert(mSignature.size()==72); updateID(); diff --git a/Transaction.h b/Transaction.h index 8cdd3cc2cc..dac0fa145f 100644 --- a/Transaction.h +++ b/Transaction.h @@ -10,6 +10,7 @@ #include "newcoin.pb.h" #include "Hanko.h" #include "Serializer.h" +#include "Wallet.h" #include "SHAMap.h" /* @@ -29,9 +30,6 @@ enum TransStatus INCOMPLETE =8 // needs more signatures }; -class Account; -class LocalAccount; - class Transaction : public boost::enable_shared_from_this { public: @@ -54,10 +52,10 @@ private: public: Transaction(); Transaction(const std::vector& rawTransaction, bool validate); - Transaction(TransStatus Status, LocalAccount& fromLocal, uint32 fromSeq, const uint160& to, uint64 amount, + Transaction(TransStatus Status, LocalAccount::pointer fromLocal, uint32 fromSeq, const uint160& to, uint64 amount, uint32 ident, uint32 ledger); - bool sign(LocalAccount& fromLocalAccount); + bool sign(LocalAccount::pointer fromLocalAccount); bool checkSign() const; void updateID(); void updateFee(); diff --git a/Wallet.cpp b/Wallet.cpp index 2273bb96ee..e19a26778b 100644 --- a/Wallet.cpp +++ b/Wallet.cpp @@ -1,185 +1,224 @@ + +#include + +#include "boost/lexical_cast.hpp" + #include "Wallet.h" #include "NewcoinAddress.h" #include "Application.h" -#include -#include -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(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::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::iterator it=mAccounts.begin(); it!=mAccounts.end(); ++it) + it->second->lock(); + } +} + +LocalAccountEntry::pointer LocalAccountFamily::get(int seq) +{ + std::map::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::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::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::iterator fit=families.find(family); + if(fit==families.end()) return LocalAccount::pointer(); + uint160 acct=fit->second->getAccount(seq); + + std::map::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 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::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(totalmAddress,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 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 - diff --git a/Wallet.h b/Wallet.h index 813b8c05dc..f4e414da7c 100644 --- a/Wallet.h +++ b/Wallet.h @@ -1,64 +1,146 @@ #ifndef __WALLET__ #define __WALLET__ -#include "keystore.h" -#include "Serializer.h" -#include "Transaction.h" -#include #include +#include +#include -class NewcoinAddress; +#include + +#include "openssl/ec.h" + +#include "uint256.h" +#include "Serializer.h" + +class LocalAccountEntry +{ // tracks keys for local accounts +public: + typedef boost::shared_ptr pointer; + +protected: + // family informaiton + uint160 mAccountFamily; + int mAccountSeq; + + // core account information + CKey::pointer mPublicKey; + CKey::pointer mPrivateKey; + uint160 mAcctID; + + // local usage tracking + uint64 mBalance; // The balance, last we checked/updated + uint32 mLedgerSeq; // The ledger seq when we updated the balance + uint32 mTxnSeq; // The sequence number of the next transaction + +public: + LocalAccountEntry(const uint160& accountFamily, int accountSeq, EC_POINT* rootPubKey); + + void unlock(BIGNUM* rootPrivKey); + void lock() { mPrivateKey=CKey::pointer(); } + + void write(); + void read(); + + const uint160& getAccountID() const { return mAcctID; } + int getAccountSeq() const { return mAccountSeq; } + std::string getLocalAccountName() const; // The name used locally to identify this account + std::string getAccountName() const; // The normal account name used to send to this account + + CKey::pointer getPubKey() { return mPublicKey; } + CKey::pointer getPrivKey() { return mPrivateKey; } + + void update(uint64 balance, uint32 seq); + uint32 getTxnSeq() const { return mTxnSeq; } + uint32 incTxnSeq() { return mTxnSeq++; } + + uint64 getBalance() const { return mBalance; } + void credit(uint64 amount) { mBalance+=amount; } + void debit(uint64 amount) { assert(mBalance>=amount); mBalance-=amount; } +}; + +class LocalAccountFamily +{ // tracks families of local accounts +public: + typedef boost::shared_ptr pointer; + +protected: + std::map mAccounts; + + uint160 mFamily; // the name for this account family + EC_POINT* rootPubKey; + + BIGNUM* rootPrivateKey; + +public: + + LocalAccountFamily(const uint160& family, EC_POINT* pubKey); + ~LocalAccountFamily(); + + const uint160& getFamily() { return mFamily; } + bool isLocked() const { return rootPrivateKey==NULL; } + void unlock(BIGNUM* privateKey); + void lock(); + + const EC_POINT* peekPubKey() const { return rootPubKey; } + + LocalAccountEntry::pointer get(int seq); + uint160 getAccount(int seq); + + std::string getPubName() const; // The text name of the public key + std::string getShortName() const; // The text name for the family +}; -/* -Keeps track of all the public/private keys you have created -*/ class LocalAccount -{ +{ // tracks a single local account public: typedef boost::shared_ptr pointer; - //CKey mKey; - //std::string mHumanAddress; - NewcoinAddress mAddress; - CKey::pointer mPublicKey; - CKey mPrivateKey; - uint160 acctID; - int64 mAmount; - uint32 mSeqNum; +protected: + LocalAccountFamily::pointer mFamily; + int mSeq; - LocalAccount(bool); // create a new local acount + +public: + LocalAccount(LocalAccountFamily::pointer fam, int seq) : mFamily(fam), mSeq(seq) { ; } + uint160 getAddress() const; + bool isLocked() const; bool signRaw(Serializer::pointer); bool signRaw(Serializer::pointer, std::vector& signature); - bool checkSignRaw(Serializer::pointer, int signaturePosition=-1, int signedData=-1); - CKey& peekPrivKey() { return mPrivateKey; } - CKey::pointer peekPubKey() { return mPublicKey; } + bool checkSignRaw(Serializer::pointer data, std::vector& signature); - uint160 getAddress() const { return mAddress.GetHash160(); } + uint32 getAcctSeq() const; + uint64 getBalance() const; + void incAcctSeq(uint32 transAcctSeq); + + CKey::pointer getPublicKey(); + CKey::pointer getPrivateKey(); }; -class Wallet : public CBasicKeyStore +class Wallet { - std::map mYourAccounts; +protected: + std::map families; + std::map accounts; - Transaction::pointer createTransaction(LocalAccount& fromAccount, const uint160& destAddr, uint64 amount); - bool commitTransaction(Transaction::pointer trans); - - LocalAccount* consolidateAccountOfSize(int64 amount); + uint160 doPrivate(const uint256& passPhrase, bool create, bool unlock); public: - Wallet(); - void refreshAccounts(); - void load(); + Wallet() { ; } - uint64 getBalance(); + uint160 addFamily(const std::string& passPhrase, bool lock); + uint160 addFamily(const uint256& passPhrase, bool lock) { return doPrivate(passPhrase, true, !lock); } + bool addFamily(const uint160& familyName, const std::string& pubKey); - // returns some human error str? - std::string sendMoneyToAddress(const uint160& destAddress, uint64 amount); + uint160 unlock(const uint256& passPhrase) { return doPrivate(passPhrase, false, true); } - // you may need to update your balances - void transactionChanged(Transaction::pointer trans); + bool lock(const uint160& familyName); + void load(void); + + LocalAccount::pointer getLocalAccount(const uint160& famBase, int seq); + LocalAccount::pointer getLocalAccount(const uint160& acctID); + std::string getPubKey(const uint160& famBase); }; -#endif \ No newline at end of file +#endif diff --git a/key.h b/key.h index c093aa0d3d..64e485d035 100644 --- a/key.h +++ b/key.h @@ -86,7 +86,6 @@ protected: EC_KEY* pkey; bool fSet; - static EC_KEY* GenerateDeterministicKey(const uint256& base, uint32 n, bool private_key); public: typedef boost::shared_ptr pointer; @@ -123,13 +122,26 @@ public: EC_KEY_free(pkey); } - CKey(const uint256& base, uint32 seq, bool private_key) : fSet(true) + + static uint256 PassPhraseToKey(const std::string& passPhrase); + static EC_KEY* GenerateRootDeterministicKey(const uint256& passPhrase); + static EC_KEY* GeneratePublicDeterministicKey(const uint160& family, EC_POINT* rootPub, int n); + static EC_KEY* GeneratePrivateDeterministicKey(const uint160& family, BIGNUM* rootPriv, int n); + + CKey(const uint256& passPhrase) : fSet(true) { - pkey=GenerateDeterministicKey(base, seq, private_key); + pkey = GenerateRootDeterministicKey(passPhrase); } - static uint256 GetBaseFromString(const std::string& base); - static uint256 GetRandomBase(void); + CKey(const uint160& base, EC_POINT* rootPubKey, int n) : fSet(true) + { // public deterministic key + pkey = GeneratePublicDeterministicKey(base, rootPubKey, n); + } + + CKey(const uint160& base, BIGNUM* rootPrivKey, int n) : fSet(true) + { // private deterministic key + pkey = GeneratePrivateDeterministicKey(base, rootPrivKey, n); + } bool IsNull() const { @@ -184,7 +196,7 @@ public: int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); if (n != nBytes) throw key_error("CKey::GetSecret(): BN_bn2bin failed"); - return vchRet; + return vchRet; } CPrivKey GetPrivKey() const