From e9ae645e3b94c6ebeec36e4f8f83aaf519ea7e12 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 11 Nov 2011 14:13:25 -0800 Subject: [PATCH] Transaction Infrastructure Work: Raw account class (address + public key) Account State class (account + balance + ledgers valid) Raw Hanko class Low-level tranasaction class Small wallet and key bits Misc updates to the protocol. Protocol addition to allow code to wait for replies, but that may not be a good idea. --- Account.h | 17 +++++++++++ AccountState.h | 16 ++++++++++ Hanko.h | 64 ++++++++++++++++++++++++++++++++++++++++ Ledger.h | 38 ++++++------------------ Makefile | 3 +- NewcoinAddress.h | 6 +--- Peer.cpp | 2 +- Transaction.cpp | 77 ++++++++++++++++++++++++++++++++---------------- Transaction.h | 28 +++++++++++++----- Wallet.h | 36 +++++++++++----------- newcoin.proto | 67 ++++++++++++++++++++++++----------------- 11 files changed, 238 insertions(+), 116 deletions(-) create mode 100644 Account.h create mode 100644 AccountState.h create mode 100644 Hanko.h diff --git a/Account.h b/Account.h new file mode 100644 index 0000000000..3146285d80 --- /dev/null +++ b/Account.h @@ -0,0 +1,17 @@ +#ifndef __ACCOUNT__ +#define __ACCOUNT__ + +class Account +{ +private: + uint160 mAddress; + CKey pubKey; + +public: + + bool CheckSignRaw(const std::vector &toSign, + const std::vector &signature) const; + const uint160& GetAddress(void) const { return mAddress; } +}; + +#endif diff --git a/AccountState.h b/AccountState.h new file mode 100644 index 0000000000..cc811caeb5 --- /dev/null +++ b/AccountState.h @@ -0,0 +1,16 @@ +#ifndef __ACCOUNTSTATE__ +#define __ACCOUNTSTATE__ + +// An account's state in one or more accepted ledgers + +class AccountState +{ +private: + int160 mAccountID; + uint64 mBalance; + uint32 mAccountSeq, mFirstValidLedger, mLastValidLedger; + +public: +}; + +#endif diff --git a/Hanko.h b/Hanko.h new file mode 100644 index 0000000000..b4a0ecc81a --- /dev/null +++ b/Hanko.h @@ -0,0 +1,64 @@ +#ifndef __HANKO__ +#define __HANKO__ + +// We use SECP256K1 http://www.secg.org/collateral/sec2_final.pdf + +#include "key.h" + +enum HankoFormat +{ + TEXT, // Hanko in text form + RAW, // Hanko in raw binary form + CONTACT, // Hanko contact block +}; + + +class Hanko +{ +public: + static const int smPubKeySize= 65; + static const int smPrivKeySize = 279; + static const int smSigSize = 57; + +private: + std::string mHanko; + std::vector mContactBlock; + CKey mPubKey; + +public: + Hanko(); + Hanko(const std::string& TextHanko); + Hanko(const std::vector& Data, HankoFormat format); + Hanko(const CKey &pubKey); + Hanko(const Hanko &); + + std::string GetHankoString(HankoFormat format) const; + std::vector GetHankoBinary(HankoFormat format) const; + + const std::vector& GetContactBlock(void) const { return mContactBlock; } + const CKey& GetPublicKey(void) const { return mPubKey; } + + int UpdateContact(std::vector& Contact); + + bool CheckHashSign(const uint256& hash, const std::vector& Signature); + bool CheckPrefixSign(const std::vector& data, uint64 type, + const std::vector &signature); +}; + + +class LocalHanko : public Hanko +{ +private: + CKey mPrivKey; + +public: + LocalHanko(std::vector &PrivKey); + LocalHanko(const CKey &Privkey); + LocalHanko(const LocalHanko &); + ~LocalHanko(); + + bool HashSign(const uint256& hash, std::vector& Signature); + bool PrefixSign(std::vector data, uint64 type, std::vector &Signature); +}; + +#endif diff --git a/Ledger.h b/Ledger.h index 184d3236a3..793fa1ab7d 100644 --- a/Ledger.h +++ b/Ledger.h @@ -4,6 +4,7 @@ #include "Transaction.h" #include "types.h" #include "BitcoinUtil.h" +#include "Hanko.h" #include #include @@ -18,39 +19,20 @@ public: typedef boost::shared_ptr pointer; typedef std::pair Account; private: - bool mValidSig; - bool mValidHash; - - uint32 mIndex; + bool mValidHash, mValidLedger, mOpen; uint256 mHash; - uint256 mSignature; - uint256 mParentHash; - uint32 mValidationSeqNum; - uint64 mFeeHeld; - std::map mAccounts; - std::list mTransactions; - std::list mDiscardedTransactions; + uint256 mParentHash, mTransHash, mAccountHash; + uint64 mFeeHeld, mTimeStamp; + uint32 mLedgerSeq; - //TransactionBundle mBundle; - - // these can be NULL - // we need to keep track in case there are changes in this ledger that effect either the parent or child. - Ledger::pointer mParent; - Ledger::pointer mChild; - - - void sign(); - void hash(); - void addTransactionAllowNeg(TransactionPtr trans); - void correctAccounts(); - void correctAccount(const uint160& address); public: Ledger(); Ledger(uint32 index); - Ledger(newcoin::FullLedger& ledger); - Ledger(Ledger::pointer other); + std::vector Sign(uint64 timestamp, LocalHanko &Hanko); + +#if 0 void setTo(newcoin::FullLedger& ledger); void mergeIn(Ledger::pointer other); @@ -61,8 +43,6 @@ public: void publishValidation(); - std::list& getTransactions(){ return(mTransactions); } - bool hasTransaction(TransactionPtr trans); int64 getAmountHeld(const uint160& address); void parentAddedTransaction(TransactionPtr cause); @@ -82,7 +62,7 @@ public: Ledger::pointer getParent(); Ledger::pointer getChild(); bool isCompatible(Ledger::pointer other); - +#endif }; diff --git a/Makefile b/Makefile index 2add02946c..f4c8218721 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,8 @@ HEADERS = \ Wallet.h SRCS= \ - Application.cpp HttpReply.cpp main.cpp RPCCommands.cpp Transaction.cpp \ + Hanko.cpp Transaction.cpp \ + Application.cpp HttpReply.cpp main.cpp RPCCommands.cpp \ BitcoinUtil.cpp keystore.cpp NewcoinAddress.cpp rpc.cpp UniqueNodeList.cpp \ CallRPC.cpp KnownNodeList.cpp PackedMessage.cpp RPCDoor.cpp ValidationCollection.cpp \ Config.cpp Ledger.cpp Peer.cpp RPCServer.cpp Wallet.cpp \ diff --git a/NewcoinAddress.h b/NewcoinAddress.h index 7b9a69af61..f3b1dc1c5b 100644 --- a/NewcoinAddress.h +++ b/NewcoinAddress.h @@ -18,15 +18,11 @@ public: bool SetHash160(const uint160& hash160); bool SetPubKey(const std::vector& vchPubKey); - bool IsValid(); uint160 GetHash160(); - - - - + std::string GetString(); }; #endif diff --git a/Peer.cpp b/Peer.cpp index 52ca5c98a8..daad40ae7c 100644 --- a/Peer.cpp +++ b/Peer.cpp @@ -209,7 +209,7 @@ void Peer::processReadBuffer() int type=PackedMessage::getType(mReadbuf); switch(type) { - case newcoin::HELLO: + case newcoin::HELLO: { newcoin::Hello hello; if(hello.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) diff --git a/Transaction.cpp b/Transaction.cpp index 8f739d2ff2..e3431e4e15 100644 --- a/Transaction.cpp +++ b/Transaction.cpp @@ -1,42 +1,67 @@ #include "Transaction.h" +#include "Wallet.h" +#include "Account.h" #include "BitcoinUtil.h" using namespace std; - -// sort by from address and then seqnum -bool gTransactionSorter(const TransactionPtr& lhs, const TransactionPtr& rhs) +Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0), + mAmount(0), mFromAccountSeq(0), mSourceLedger(0), mIdent(0), + mInLedger(0), mStatus(INVALID) { - if(lhs->from() == rhs->from()) - { - return(lhs->seqnum() < rhs->seqnum() ); - }else return lhs->from() < rhs->from(); } -// I don't think we need to bother checking the sig or public key -bool Transaction::isEqual(TransactionPtr t1,TransactionPtr t2) +Transaction::Transaction(TransStatus status, LocalAccount &fromLocalAccount, const Account &fromAccount, + 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) { - if(t1->amount() != t2->amount()) return(false); - if(t1->seqnum() != t2->seqnum()) return(false); - if(t1->ledgerindex() != t2->ledgerindex()) return(false); - if(t1->from() != t2->from()) return(false); - if(t1->dest() != t2->dest()) return(false); + assert(fromAccount.GetAddress()==fromLocalAccount.mAddress); + assert(fromLocalAccount.mAmount>=amount); + assert((fromSeq+1)==fromLocalAccount.mSeqNum); - return(true); + mAccountFrom=fromAccount.GetAddress(); + Sign(fromLocalAccount, fromAccount); } - -uint256 Transaction::calcHash(TransactionPtr trans) +bool Transaction::Sign(LocalAccount &fromLocalAccount, const Account &fromAccount) { - vector buffer; - buffer.resize(trans->ByteSize()); - trans->SerializeToArray(&(buffer[0]),buffer.size()); - return Hash(buffer.begin(), buffer.end()); + if( (mAmount==0) || (mSourceLedger==0) || (mAccountTo==0) ) + return false; + if((mAccountFrom!=fromLocalAccount.mAddress)||(mAccountFrom!=fromAccount.GetAddress())) + return false; + + UpdateHash(); + + std::vector toSign, Signature; + if(!GetRawUnsigned(toSign, fromAccount)) return false; + if(!fromLocalAccount.SignRaw(toSign, Signature)) return false; + mSignature=Signature; + return true; } - -bool Transaction::isSigValid(TransactionPtr trans) +bool Transaction::CheckSign(const Account &fromAccount) const { - // TODO: Transaction::isSigValid - return(true); -} \ No newline at end of file + if(mAccountFrom!=fromAccount.GetAddress()) return false; + + std::vector toSign; + if(!GetRawUnsigned(toSign, fromAccount)) return false; + + return fromAccount.CheckSignRaw(toSign, mSignature); +} + +bool Transaction::GetRawUnsigned(std::vector &raw, const Account &fromAccount) const +{ + raw.clear(); + +} + +#if 0 +void Transaction::UpdateHash() +{ // FIXME + vector buffer; + buffer.resize(trans->ByteSize()); + trans->SerializeToArray(&(buffer[0]),buffer.size()); + return Hash(buffer.begin(), buffer.end()); +} +#endif diff --git a/Transaction.h b/Transaction.h index cf83346994..7068bb09d4 100644 --- a/Transaction.h +++ b/Transaction.h @@ -3,7 +3,10 @@ #include "uint256.h" #include "newcoin.pb.h" +#include "Hanko.h" #include +#include +#include /* We could have made something that inherited from the protobuf transaction but this seemed simpler @@ -11,6 +14,7 @@ We could have made something that inherited from the protobuf transaction but th enum TransStatus { + NEW, // just received / generated INVALID, // no valid signature, insufficient funds INCLUDED, // added to the current ledger CONFLICTED, // losing to a conflicting transaction @@ -18,11 +22,13 @@ enum TransStatus HELD, // not valid now, maybe later }; +class Account; +class LocalAccount; -class Transaction +class Transaction : public boost::enable_shared_from_this { public: - typedef boost::shared_ptr pointer; + static const uint32 TransSignMagic=0x54584E00; // "TXN" private: uint256 mTransactionID; @@ -34,17 +40,20 @@ private: uint32 mInLedger; TransStatus mStatus; + void UpdateHash(void); + public: Transaction(); - Transaction(const uint256 &id); Transaction(const std::vector rawTransaction); Transaction(const std::string sqlReply); - - Transaction(Account &from, Account &to, uint64 amount, uint32 ident, uint32 ledger); - bool Sign(const std::vector& privKey); + Transaction(TransStatus Status, LocalAccount &fromLocal, const Account &from, + uint32 fromSeq, const uint160 &to, uint64 amount, uint32 ident, uint32 ledger); - const std::string getSQL() const; - bool checkSignature(void) const; + bool Sign(LocalAccount &fromLocalAccount, const Account &fromAccount); + bool CheckSign(const Account &fromAccount) const; + + bool GetRawUnsigned(std::vector &raw, const Account &from) const; + bool GetRawSigned(std::vector &raw, const Account &from) const; const uint256& GetID() const { return mTransactionID; } const uint160& GetFromAccount() const { return mAccountFrom; } @@ -58,6 +67,7 @@ public: TransStatus GetStatus() const { return mStatus; } void SetStatus(TransStatus st); + void SetLedger(uint32 Ledger); bool operator<(const Transaction &) const; bool operator>(const Transaction &) const; @@ -67,4 +77,6 @@ public: bool operator>=(const Transaction &) const; }; +typedef boost::shared_ptr TransactionPtr; + #endif diff --git a/Wallet.h b/Wallet.h index 4bfc2c63a1..782a489709 100644 --- a/Wallet.h +++ b/Wallet.h @@ -12,31 +12,30 @@ class NewcoinAddress; /* Keeps track of all the public/private keys you have created */ +class LocalAccount +{ +public: + //CKey mKey; + //std::string mHumanAddress; + uint160 mAddress; + CKey mPublicKey, mPrivateKey; + int64 mAmount; + uint32 mSeqNum; + + bool SignRaw(const std::vector &toSign, std::vector &signature); + bool CheckSignRaw(const std::vector &toSign, const std::vector &signature); +}; + class Wallet : public CBasicKeyStore { - class Account - { - public: - //CKey mKey; - //std::string mHumanAddress; - uint160 mAddress; - std::vector mPublicKey; - std::vector mPrivateKey; - int64 mAmount; - uint32 mSeqNum; - - - Account(){} - bool signTransaction(TransactionPtr input); - }; - std::list mYourAccounts; + std::list mYourAccounts; - TransactionPtr createTransaction(Account& fromAccount, uint160& destAddr, int64 amount); + TransactionPtr createTransaction(LocalAccount& fromAccount, uint160& destAddr, int64 amount); bool commitTransaction(TransactionPtr trans); - Account* consolidateAccountOfSize(int64 amount); + LocalAccount* consolidateAccountOfSize(int64 amount); public: Wallet(); @@ -51,7 +50,6 @@ public: // you may need to update your balances void transactionChanged(TransactionPtr trans); - }; #endif \ No newline at end of file diff --git a/newcoin.proto b/newcoin.proto index 7264f23101..762daa5e96 100644 --- a/newcoin.proto +++ b/newcoin.proto @@ -1,33 +1,46 @@ package newcoin; -enum Type { -// abnormal +enum MessageType { +// core + HELLO= 0; ERROR_MSG= 1; + PING= 2; -// session management - HELLO= 10; - PING= 11; +// network presence detection + GET_CONTACTS= 10; + CONTACT= 11; -// transaction processing - TRANSACTION= 20; -// ledger closing - PROPOSE_LEDGER= 30; - CLOSE_LEDGER= 31; +// operations for 'small' nodes + SEARCH_TRANSACTION= 20; + GET_ACCOUNT= 21; + ACCOUNT= 22; + +// transaction and ledger processing + TRANSACTION= 30; + GET_LEDGER= 31; + LEDGER= 32; + PROPOSE_LEDGER= 33; + CLOSE_LEDGER= 34; // data replication and synchronization GET_VALIDATIONS= 40; VALIDATION= 41; - GET_CONTACTS= 42; - CONTACT= 43; - GET_OBJECT= 44; - OBJECT= 45; + GET_OBJECT= 42; + OBJECT= 43; +} + +message TMBaseMessage { + required MessageType type = 1; + optional uint32 seq = 2; + optional uint32 querySeq = 3; // if this is a reply + required bytes innerMessage = 4; } // Sent on connect -message Hello { +message TMHello { required uint32 version = 1; optional uint32 ledgerIndex = 2; optional uint64 netTime = 3; @@ -41,7 +54,7 @@ If you want to send an amount that is greater than any single address of yours you must first combine coins from one address to another. */ -message Transaction { +message TMTransaction { required bytes from = 1; required bytes dest = 2; required uint64 amount = 3; @@ -55,7 +68,7 @@ message Transaction { // Used to propose/validate during ledger close -message Validation { +message TMValidation { required uint32 ledgerIndex = 1; required bytes ledgerHash = 2; optional uint64 timestamp = 3; // only in proposed ledgers @@ -66,7 +79,7 @@ message Validation { -message GetValidations { +message TMGetValidations { required uint32 ledgerIndex = 1; repeated bytes hanko = 2; optional uint32 count = 3; // get random validations @@ -74,7 +87,7 @@ message GetValidations { -message Contact { +message TMContact { required bytes pubKey = 1; required uint32 softwareVersion = 2; required uint32 protoVersion = 3; @@ -85,14 +98,14 @@ message Contact { } // request node information -message GetContacts { +message TMGetContacts { repeated bytes nodeIDs =1; // specific nodes we want optional uint32 nodeCount =2; // get some random nodes } -message IndexedObject +message TMIndexedObject { enum ObjectType { TRANSACTION = 1; @@ -110,23 +123,23 @@ message IndexedObject -message GetObjectByHash +message TMGetObjectByHash { - required IndexedObject object = 1; + required TMIndexedObject object = 1; } -message ObjectByHash +message TMObjectByHash { - required IndexedObject object = 1; + required TMIndexedObject object = 1; required bytes data = 2; } -message Ping { +message TMPing { enum pingType { PING = 0; // we want a reply PONG = 1; // this is a reply @@ -138,7 +151,7 @@ message Ping { } -message ErrorMsg { +message TMErrorMsg { optional int32 errorCode = 1; optional string message = 2; }