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.
This commit is contained in:
JoelKatz
2011-11-11 14:13:25 -08:00
parent fd7e41501b
commit e9ae645e3b
11 changed files with 238 additions and 116 deletions

17
Account.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef __ACCOUNT__
#define __ACCOUNT__
class Account
{
private:
uint160 mAddress;
CKey pubKey;
public:
bool CheckSignRaw(const std::vector<unsigned char> &toSign,
const std::vector<unsigned char> &signature) const;
const uint160& GetAddress(void) const { return mAddress; }
};
#endif

16
AccountState.h Normal file
View File

@@ -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

64
Hanko.h Normal file
View File

@@ -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<unsigned char> mContactBlock;
CKey mPubKey;
public:
Hanko();
Hanko(const std::string& TextHanko);
Hanko(const std::vector<unsigned char>& Data, HankoFormat format);
Hanko(const CKey &pubKey);
Hanko(const Hanko &);
std::string GetHankoString(HankoFormat format) const;
std::vector<unsigned char> GetHankoBinary(HankoFormat format) const;
const std::vector<unsigned char>& GetContactBlock(void) const { return mContactBlock; }
const CKey& GetPublicKey(void) const { return mPubKey; }
int UpdateContact(std::vector<unsigned char>& Contact);
bool CheckHashSign(const uint256& hash, const std::vector<unsigned char>& Signature);
bool CheckPrefixSign(const std::vector<unsigned char>& data, uint64 type,
const std::vector<unsigned char> &signature);
};
class LocalHanko : public Hanko
{
private:
CKey mPrivKey;
public:
LocalHanko(std::vector<unsigned char> &PrivKey);
LocalHanko(const CKey &Privkey);
LocalHanko(const LocalHanko &);
~LocalHanko();
bool HashSign(const uint256& hash, std::vector<unsigned char>& Signature);
bool PrefixSign(std::vector<unsigned char> data, uint64 type, std::vector<unsigned char> &Signature);
};
#endif

View File

@@ -4,6 +4,7 @@
#include "Transaction.h" #include "Transaction.h"
#include "types.h" #include "types.h"
#include "BitcoinUtil.h" #include "BitcoinUtil.h"
#include "Hanko.h"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp> #include <boost/enable_shared_from_this.hpp>
@@ -18,39 +19,20 @@ public:
typedef boost::shared_ptr<Ledger> pointer; typedef boost::shared_ptr<Ledger> pointer;
typedef std::pair<int64,uint32> Account; typedef std::pair<int64,uint32> Account;
private: private:
bool mValidSig; bool mValidHash, mValidLedger, mOpen;
bool mValidHash;
uint32 mIndex;
uint256 mHash; uint256 mHash;
uint256 mSignature;
uint256 mParentHash;
uint32 mValidationSeqNum;
uint64 mFeeHeld;
std::map<uint160, Account > mAccounts; uint256 mParentHash, mTransHash, mAccountHash;
std::list<TransactionPtr> mTransactions; uint64 mFeeHeld, mTimeStamp;
std::list<TransactionPtr> mDiscardedTransactions; 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: public:
Ledger(); Ledger();
Ledger(uint32 index); Ledger(uint32 index);
Ledger(newcoin::FullLedger& ledger);
Ledger(Ledger::pointer other);
std::vector<unsigned char> Sign(uint64 timestamp, LocalHanko &Hanko);
#if 0
void setTo(newcoin::FullLedger& ledger); void setTo(newcoin::FullLedger& ledger);
void mergeIn(Ledger::pointer other); void mergeIn(Ledger::pointer other);
@@ -61,8 +43,6 @@ public:
void publishValidation(); void publishValidation();
std::list<TransactionPtr>& getTransactions(){ return(mTransactions); }
bool hasTransaction(TransactionPtr trans); bool hasTransaction(TransactionPtr trans);
int64 getAmountHeld(const uint160& address); int64 getAmountHeld(const uint160& address);
void parentAddedTransaction(TransactionPtr cause); void parentAddedTransaction(TransactionPtr cause);
@@ -82,7 +62,7 @@ public:
Ledger::pointer getParent(); Ledger::pointer getParent();
Ledger::pointer getChild(); Ledger::pointer getChild();
bool isCompatible(Ledger::pointer other); bool isCompatible(Ledger::pointer other);
#endif
}; };

View File

@@ -67,7 +67,8 @@ HEADERS = \
Wallet.h Wallet.h
SRCS= \ 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 \ BitcoinUtil.cpp keystore.cpp NewcoinAddress.cpp rpc.cpp UniqueNodeList.cpp \
CallRPC.cpp KnownNodeList.cpp PackedMessage.cpp RPCDoor.cpp ValidationCollection.cpp \ CallRPC.cpp KnownNodeList.cpp PackedMessage.cpp RPCDoor.cpp ValidationCollection.cpp \
Config.cpp Ledger.cpp Peer.cpp RPCServer.cpp Wallet.cpp \ Config.cpp Ledger.cpp Peer.cpp RPCServer.cpp Wallet.cpp \

View File

@@ -18,15 +18,11 @@ public:
bool SetHash160(const uint160& hash160); bool SetHash160(const uint160& hash160);
bool SetPubKey(const std::vector<unsigned char>& vchPubKey); bool SetPubKey(const std::vector<unsigned char>& vchPubKey);
bool IsValid(); bool IsValid();
uint160 GetHash160(); uint160 GetHash160();
std::string GetString();
}; };
#endif #endif

View File

@@ -209,7 +209,7 @@ void Peer::processReadBuffer()
int type=PackedMessage::getType(mReadbuf); int type=PackedMessage::getType(mReadbuf);
switch(type) switch(type)
{ {
case newcoin::HELLO: case newcoin::HELLO:
{ {
newcoin::Hello hello; newcoin::Hello hello;
if(hello.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) if(hello.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))

View File

@@ -1,42 +1,67 @@
#include "Transaction.h" #include "Transaction.h"
#include "Wallet.h"
#include "Account.h"
#include "BitcoinUtil.h" #include "BitcoinUtil.h"
using namespace std; using namespace std;
Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0),
// sort by from address and then seqnum mAmount(0), mFromAccountSeq(0), mSourceLedger(0), mIdent(0),
bool gTransactionSorter(const TransactionPtr& lhs, const TransactionPtr& rhs) 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 Transaction::Transaction(TransStatus status, LocalAccount &fromLocalAccount, const Account &fromAccount,
bool Transaction::isEqual(TransactionPtr t1,TransactionPtr t2) 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); assert(fromAccount.GetAddress()==fromLocalAccount.mAddress);
if(t1->seqnum() != t2->seqnum()) return(false); assert(fromLocalAccount.mAmount>=amount);
if(t1->ledgerindex() != t2->ledgerindex()) return(false); assert((fromSeq+1)==fromLocalAccount.mSeqNum);
if(t1->from() != t2->from()) return(false);
if(t1->dest() != t2->dest()) return(false);
return(true); mAccountFrom=fromAccount.GetAddress();
Sign(fromLocalAccount, fromAccount);
} }
bool Transaction::Sign(LocalAccount &fromLocalAccount, const Account &fromAccount)
uint256 Transaction::calcHash(TransactionPtr trans)
{ {
vector<unsigned char> buffer; if( (mAmount==0) || (mSourceLedger==0) || (mAccountTo==0) )
buffer.resize(trans->ByteSize()); return false;
trans->SerializeToArray(&(buffer[0]),buffer.size()); if((mAccountFrom!=fromLocalAccount.mAddress)||(mAccountFrom!=fromAccount.GetAddress()))
return Hash(buffer.begin(), buffer.end()); return false;
UpdateHash();
std::vector<unsigned char> toSign, Signature;
if(!GetRawUnsigned(toSign, fromAccount)) return false;
if(!fromLocalAccount.SignRaw(toSign, Signature)) return false;
mSignature=Signature;
return true;
} }
bool Transaction::CheckSign(const Account &fromAccount) const
bool Transaction::isSigValid(TransactionPtr trans)
{ {
// TODO: Transaction::isSigValid if(mAccountFrom!=fromAccount.GetAddress()) return false;
return(true);
} std::vector<unsigned char> toSign;
if(!GetRawUnsigned(toSign, fromAccount)) return false;
return fromAccount.CheckSignRaw(toSign, mSignature);
}
bool Transaction::GetRawUnsigned(std::vector<unsigned char> &raw, const Account &fromAccount) const
{
raw.clear();
}
#if 0
void Transaction::UpdateHash()
{ // FIXME
vector<unsigned char> buffer;
buffer.resize(trans->ByteSize());
trans->SerializeToArray(&(buffer[0]),buffer.size());
return Hash(buffer.begin(), buffer.end());
}
#endif

View File

@@ -3,7 +3,10 @@
#include "uint256.h" #include "uint256.h"
#include "newcoin.pb.h" #include "newcoin.pb.h"
#include "Hanko.h"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/cstdint.hpp>
/* /*
We could have made something that inherited from the protobuf transaction but this seemed simpler 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 enum TransStatus
{ {
NEW, // just received / generated
INVALID, // no valid signature, insufficient funds INVALID, // no valid signature, insufficient funds
INCLUDED, // added to the current ledger INCLUDED, // added to the current ledger
CONFLICTED, // losing to a conflicting transaction CONFLICTED, // losing to a conflicting transaction
@@ -18,11 +22,13 @@ enum TransStatus
HELD, // not valid now, maybe later HELD, // not valid now, maybe later
}; };
class Account;
class LocalAccount;
class Transaction class Transaction : public boost::enable_shared_from_this<Transaction>
{ {
public: public:
typedef boost::shared_ptr<Transaction> pointer; static const uint32 TransSignMagic=0x54584E00; // "TXN"
private: private:
uint256 mTransactionID; uint256 mTransactionID;
@@ -34,17 +40,20 @@ private:
uint32 mInLedger; uint32 mInLedger;
TransStatus mStatus; TransStatus mStatus;
void UpdateHash(void);
public: public:
Transaction(); Transaction();
Transaction(const uint256 &id);
Transaction(const std::vector<unsigned char> rawTransaction); Transaction(const std::vector<unsigned char> rawTransaction);
Transaction(const std::string sqlReply); Transaction(const std::string sqlReply);
Transaction(TransStatus Status, LocalAccount &fromLocal, const Account &from,
Transaction(Account &from, Account &to, uint64 amount, uint32 ident, uint32 ledger); uint32 fromSeq, const uint160 &to, uint64 amount, uint32 ident, uint32 ledger);
bool Sign(const std::vector<unsigned char>& privKey);
const std::string getSQL() const; bool Sign(LocalAccount &fromLocalAccount, const Account &fromAccount);
bool checkSignature(void) const; bool CheckSign(const Account &fromAccount) const;
bool GetRawUnsigned(std::vector<unsigned char> &raw, const Account &from) const;
bool GetRawSigned(std::vector<unsigned char> &raw, const Account &from) const;
const uint256& GetID() const { return mTransactionID; } const uint256& GetID() const { return mTransactionID; }
const uint160& GetFromAccount() const { return mAccountFrom; } const uint160& GetFromAccount() const { return mAccountFrom; }
@@ -58,6 +67,7 @@ public:
TransStatus GetStatus() const { return mStatus; } TransStatus GetStatus() const { return mStatus; }
void SetStatus(TransStatus st); void SetStatus(TransStatus st);
void SetLedger(uint32 Ledger);
bool operator<(const Transaction &) const; bool operator<(const Transaction &) const;
bool operator>(const Transaction &) const; bool operator>(const Transaction &) const;
@@ -67,4 +77,6 @@ public:
bool operator>=(const Transaction &) const; bool operator>=(const Transaction &) const;
}; };
typedef boost::shared_ptr<Transaction> TransactionPtr;
#endif #endif

View File

@@ -12,31 +12,30 @@ class NewcoinAddress;
/* /*
Keeps track of all the public/private keys you have created 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<unsigned char> &toSign, std::vector<unsigned char> &signature);
bool CheckSignRaw(const std::vector<unsigned char> &toSign, const std::vector<unsigned char> &signature);
};
class Wallet : public CBasicKeyStore class Wallet : public CBasicKeyStore
{ {
class Account std::list<LocalAccount> mYourAccounts;
{
public:
//CKey mKey;
//std::string mHumanAddress;
uint160 mAddress;
std::vector<unsigned char> mPublicKey;
std::vector<unsigned char> mPrivateKey;
int64 mAmount;
uint32 mSeqNum;
Account(){}
bool signTransaction(TransactionPtr input);
};
std::list<Account> mYourAccounts;
TransactionPtr createTransaction(Account& fromAccount, uint160& destAddr, int64 amount); TransactionPtr createTransaction(LocalAccount& fromAccount, uint160& destAddr, int64 amount);
bool commitTransaction(TransactionPtr trans); bool commitTransaction(TransactionPtr trans);
Account* consolidateAccountOfSize(int64 amount); LocalAccount* consolidateAccountOfSize(int64 amount);
public: public:
Wallet(); Wallet();
@@ -51,7 +50,6 @@ public:
// you may need to update your balances // you may need to update your balances
void transactionChanged(TransactionPtr trans); void transactionChanged(TransactionPtr trans);
}; };
#endif #endif

View File

@@ -1,33 +1,46 @@
package newcoin; package newcoin;
enum Type { enum MessageType {
// abnormal // core
HELLO= 0;
ERROR_MSG= 1; ERROR_MSG= 1;
PING= 2;
// session management // network presence detection
HELLO= 10; GET_CONTACTS= 10;
PING= 11; CONTACT= 11;
// transaction processing
TRANSACTION= 20;
// ledger closing // operations for 'small' nodes
PROPOSE_LEDGER= 30; SEARCH_TRANSACTION= 20;
CLOSE_LEDGER= 31; 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 // data replication and synchronization
GET_VALIDATIONS= 40; GET_VALIDATIONS= 40;
VALIDATION= 41; VALIDATION= 41;
GET_CONTACTS= 42; GET_OBJECT= 42;
CONTACT= 43; OBJECT= 43;
GET_OBJECT= 44; }
OBJECT= 45;
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 // Sent on connect
message Hello { message TMHello {
required uint32 version = 1; required uint32 version = 1;
optional uint32 ledgerIndex = 2; optional uint32 ledgerIndex = 2;
optional uint64 netTime = 3; 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. you must first combine coins from one address to another.
*/ */
message Transaction { message TMTransaction {
required bytes from = 1; required bytes from = 1;
required bytes dest = 2; required bytes dest = 2;
required uint64 amount = 3; required uint64 amount = 3;
@@ -55,7 +68,7 @@ message Transaction {
// Used to propose/validate during ledger close // Used to propose/validate during ledger close
message Validation { message TMValidation {
required uint32 ledgerIndex = 1; required uint32 ledgerIndex = 1;
required bytes ledgerHash = 2; required bytes ledgerHash = 2;
optional uint64 timestamp = 3; // only in proposed ledgers optional uint64 timestamp = 3; // only in proposed ledgers
@@ -66,7 +79,7 @@ message Validation {
message GetValidations { message TMGetValidations {
required uint32 ledgerIndex = 1; required uint32 ledgerIndex = 1;
repeated bytes hanko = 2; repeated bytes hanko = 2;
optional uint32 count = 3; // get random validations optional uint32 count = 3; // get random validations
@@ -74,7 +87,7 @@ message GetValidations {
message Contact { message TMContact {
required bytes pubKey = 1; required bytes pubKey = 1;
required uint32 softwareVersion = 2; required uint32 softwareVersion = 2;
required uint32 protoVersion = 3; required uint32 protoVersion = 3;
@@ -85,14 +98,14 @@ message Contact {
} }
// request node information // request node information
message GetContacts { message TMGetContacts {
repeated bytes nodeIDs =1; // specific nodes we want repeated bytes nodeIDs =1; // specific nodes we want
optional uint32 nodeCount =2; // get some random nodes optional uint32 nodeCount =2; // get some random nodes
} }
message IndexedObject message TMIndexedObject
{ {
enum ObjectType { enum ObjectType {
TRANSACTION = 1; 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; required bytes data = 2;
} }
message Ping { message TMPing {
enum pingType { enum pingType {
PING = 0; // we want a reply PING = 0; // we want a reply
PONG = 1; // this is a reply PONG = 1; // this is a reply
@@ -138,7 +151,7 @@ message Ping {
} }
message ErrorMsg { message TMErrorMsg {
optional int32 errorCode = 1; optional int32 errorCode = 1;
optional string message = 2; optional string message = 2;
} }