Merge with Bitcoin v0.6.3
authorScott Nadal <scott.nadal@gmail.com>
Mon, 23 Jul 2012 00:09:18 +0000 (01:09 +0100)
committerScott Nadal <scott.nadal@gmail.com>
Mon, 23 Jul 2012 00:09:18 +0000 (01:09 +0100)
49 files changed:
README.ppcoin [new file with mode: 0644]
bitcointools/.gitignore [new file with mode: 0644]
bitcointools/BCDataStream.py [new file with mode: 0644]
bitcointools/LICENSE.txt [new file with mode: 0644]
bitcointools/NOTES.txt [new file with mode: 0644]
bitcointools/README.txt [new file with mode: 0644]
bitcointools/__init__.py [new file with mode: 0644]
bitcointools/address.py [new file with mode: 0644]
bitcointools/base58.py [new file with mode: 0644]
bitcointools/blkindex.py [new file with mode: 0644]
bitcointools/block.py [new file with mode: 0644]
bitcointools/blocks.py [new file with mode: 0644]
bitcointools/dbdump.py [new file with mode: 0755]
bitcointools/deserialize.py [new file with mode: 0644]
bitcointools/enumeration.py [new file with mode: 0644]
bitcointools/fixwallet.py [new file with mode: 0755]
bitcointools/jsonToCSV.py [new file with mode: 0755]
bitcointools/statistics.py [new file with mode: 0755]
bitcointools/testBCDataStream.py [new file with mode: 0644]
bitcointools/transaction.py [new file with mode: 0644]
bitcointools/util.py [new file with mode: 0644]
bitcointools/wallet.py [new file with mode: 0644]
src/base58.h
src/bignum.h
src/bitcoinrpc.cpp
src/checkpoints.cpp
src/checkpoints.h
src/db.cpp
src/db.h
src/init.cpp
src/main.cpp
src/main.h
src/makefile.mingw
src/makefile.unix
src/net.cpp
src/net.h
src/ppcoin/genesis.cpp [new file with mode: 0644]
src/ppcoin/obj/.gitignore [new file with mode: 0644]
src/protocol.h
src/qt/bitcoinunits.cpp
src/script.cpp
src/script.h
src/serialize.h
src/util.cpp
src/util.h
src/version.cpp
src/version.h
src/wallet.cpp
src/wallet.h

diff --git a/README.ppcoin b/README.ppcoin
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bitcointools/.gitignore b/bitcointools/.gitignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/bitcointools/BCDataStream.py b/bitcointools/BCDataStream.py
new file mode 100644 (file)
index 0000000..cb2d1d0
--- /dev/null
@@ -0,0 +1,116 @@
+#
+# Workalike python implementation of Bitcoin's CDataStream class.
+#
+import struct
+import StringIO
+import mmap
+
+class SerializationError(Exception):
+  """ Thrown when there's a problem deserializing or serializing """
+
+class BCDataStream(object):
+  def __init__(self):
+    self.input = None
+    self.read_cursor = 0
+
+  def clear(self):
+    self.input = None
+    self.read_cursor = 0
+
+  def write(self, bytes):  # Initialize with string of bytes
+    if self.input is None:
+      self.input = bytes
+    else:
+      self.input += bytes
+
+  def map_file(self, file, start):  # Initialize with bytes from file
+    self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
+    self.read_cursor = start
+  def seek_file(self, position):
+    self.read_cursor = position
+  def close_file(self):
+    self.input.close()
+
+  def read_string(self):
+    # Strings are encoded depending on length:
+    # 0 to 252 :  1-byte-length followed by bytes (if any)
+    # 253 to 65,535 : byte'253' 2-byte-length followed by bytes
+    # 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes
+    # ... and the Bitcoin client is coded to understand:
+    # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
+    # ... but I don't think it actually handles any strings that big.
+    if self.input is None:
+      raise SerializationError("call write(bytes) before trying to deserialize")
+
+    try:
+      length = self.read_compact_size()
+    except IndexError:
+      raise SerializationError("attempt to read past end of buffer")
+
+    return self.read_bytes(length)
+
+  def write_string(self, string):
+    # Length-encoded as with read-string
+    self.write_compact_size(len(string))
+    self.write(string)
+
+  def read_bytes(self, length):
+    try:
+      result = self.input[self.read_cursor:self.read_cursor+length]
+      self.read_cursor += length
+      return result
+    except IndexError:
+      raise SerializationError("attempt to read past end of buffer")
+
+    return ''
+
+  def read_boolean(self): return self.read_bytes(1)[0] != chr(0)
+  def read_int16(self): return self._read_num('<h')
+  def read_uint16(self): return self._read_num('<H')
+  def read_int32(self): return self._read_num('<i')
+  def read_uint32(self): return self._read_num('<I')
+  def read_int64(self): return self._read_num('<q')
+  def read_uint64(self): return self._read_num('<Q')
+
+  def write_boolean(self, val): return self.write(chr(1) if val else chr(0))
+  def write_int16(self, val): return self._write_num('<h', val)
+  def write_uint16(self, val): return self._write_num('<H', val)
+  def write_int32(self, val): return self._write_num('<i', val)
+  def write_uint32(self, val): return self._write_num('<I', val)
+  def write_int64(self, val): return self._write_num('<q', val)
+  def write_uint64(self, val): return self._write_num('<Q', val)
+
+  def read_compact_size(self):
+    size = ord(self.input[self.read_cursor])
+    self.read_cursor += 1
+    if size == 253:
+      size = self._read_num('<H')
+    elif size == 254:
+      size = self._read_num('<I')
+    elif size == 255:
+      size = self._read_num('<Q')
+    return size
+
+  def write_compact_size(self, size):
+    if size < 0:
+      raise SerializationError("attempt to write size < 0")
+    elif size < 253:
+       self.write(chr(size))
+    elif size < 2**16:
+      self.write('\xfd')
+      self._write_num('<H', size)
+    elif size < 2**32:
+      self.write('\xfe')
+      self._write_num('<I', size)
+    elif size < 2**64:
+      self.write('\xff')
+      self._write_num('<Q', size)
+
+  def _read_num(self, format):
+    (i,) = struct.unpack_from(format, self.input, self.read_cursor)
+    self.read_cursor += struct.calcsize(format)
+    return i
+
+  def _write_num(self, format, num):
+    s = struct.pack(format, num)
+    self.write(s)
diff --git a/bitcointools/LICENSE.txt b/bitcointools/LICENSE.txt
new file mode 100644 (file)
index 0000000..31b5b47
--- /dev/null
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Gavin Andresen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bitcointools/NOTES.txt b/bitcointools/NOTES.txt
new file mode 100644 (file)
index 0000000..2531c93
--- /dev/null
@@ -0,0 +1,367 @@
+Most of this information is now at the Protocol Specification page at the bitcoin wiki:
+  https://en.bitcoin.it/wiki/Protocol_specification
+
+One of these days I might check this document against the wiki, add any missing information to the wiki, and remove the information below.
+
+
+Transaction dissection notes:
+-----------------------------
+
+My first two transactions look like this:
+
+==WalletTransaction== f240...2582
+Merkle hashBlock: 6d40...0000
+1 tx in, 1 out
+['TxIn: prev(e9d4...d2f4:0) pubkey: 1Lfic7L3z7xUbxRDvMNFiE3QxKRmSSrvEH sig: 72:3045...1a01 65:0403...7b86']
+['TxOut: value: 500.00 pubkey: 19vcWM6EEbQHVdN2W8NXv9ySgsPjbZ6gU3 Script: DUP HASH160 20:61e4...614c EQUALVERIFY CHECKSIG']
+ timeReceived:Thu May 27 14:25:48 2010 fromMe:True spent:True
+
+==WalletTransaction== f961...4472
+Merkle hashBlock: 7707...0000
+1 tx in, 2 out
+['TxIn: prev(f240...2582:0) pubkey: 19vcWM6EEbQHVdN2W8NXv9ySgsPjbZ6gU3 sig: 72:3045...a801 65:04a6...a541']
+['TxOut: value: 142.40 pubkey: 15F9miF9egehbeCiPKVSJSwgw5J96my7Rf Script: DUP HASH160 20:2e8d...5179 EQUALVERIFY CHECKSIG', 'TxOut: value: 357.60 pubkey: 1Av6tJSFv3bZvUfaALm4TQZmYvdeuHcbUb Script: DUP HASH160 20:6cc4...1755 EQUALVERIFY CHECKSIG']
+ timeReceived:Fri May 28 13:55:23 2010 fromMe:True spent:True
+
+So: whoever has sending bc address 1Lfic7L3z7xUbxRDvMNFiE3QxKRmSSrvEH sent 500 bitcoins to
+my 19vcWM6EEbQHVdN2W8NXv9ySgsPjbZ6gU3 receiving address.
+
+I then sent 142.40 to bcaddress 15F9miF9egehbeCiPKVSJSwgw5J96my7Rf.
+And got 357.60 in change (Bitcoin created a new address for the change:  1Av6tJSFv3bZvUfaALm4TQZmYvdeuHcbUb)
+
+Spending generated coins looks different:
+
+Transaction 6403...cc0e
+['TxIn: COIN GENERATED coinbase:0464ba0e1c010a']
+['TxOut: value: 50.00 pubkey: 1Cpr8WJYa5igdo8AtPS24hfVLkk6ANZSWN Script: 65:0442...11c2 CHECKSIG']
+
+Transaction db39...8dcd:
+['TxIn: COIN GENERATED coinbase:0464ba0e1c0130']
+['TxOut: value: 50.00 pubkey: 1LuamEE1rL1QAdgn48gGFD9T9mnqSeP17w Script: 65:04f2...e10f CHECKSIG']
+
+==WalletTransaction== 3860...f888
+['TxIn: prev(6403...cc0e:0) pubkey: (None) sig: 72:3045...f501', 'TxIn: prev(db39...8dcd:0) pubkey: (None) sig: 73:3046...9\
+601']
+['TxOut: value: 100.00 pubkey: 1BBWNxMqU2bTwSVeUDVKmNYJrWr4D1pBrf Script: DUP HASH160 20:6fad...ab90 EQUALVERIFY CHECKSIG']
+
+Here I received 100 bitcoins from two 50 COIN GENERATED transactions.
+
+
+blkindex.dat serialization notes:
+---------------------------------
+
+Keys beging with a serialized string, rest of key and value depend on that first string.  Possibilities are:
+
+tx : key continues with uint256 transaction hash/id (32 bytes)
+ value is:
+  version : 32-bit unsigned int
+  CDiskTxPos : location of transaction (in the blk000N.dat files)
+  vector<CDiskTxPos> : location of transactions that spend this transaction's inputs
+
+blockindex : key continues with uint256 block hash (32 bytes)
+ value is serialized CDiskBlockIndex
+
+version : value is integer version number
+
+hashBestChain : value is 32-byte SHA256 hash of last block in the longest block chain.
+
+
+wallet.dat serialization notes:
+-------------------------------
+Berkely DB BTREE file (key-value pairs).
+
+Keys begin with a serialized string, rest of key and value depend on that first string.  Possibilities are:
+
+name : key continues with 34-character Bitcoin "hash160" address
+ value is a simple string.
+
+tx : key continues with uint256 (32 bytes)
+  value is a serialized CTransaction+CMerkleTx+CWalletTx  (see below for details)
+
+key : key continues with vector<unsigned char> that is public key
+  value is vector<unsigned char> that is private key (full 271-byte
+    OpenSSL representation)
+
+wkey : key continues with vector<unsigned char> that is public key
+  value is serialized CWalletKey:
+    vector<unsigned char> that is private key
+    int64 nTimeCreated
+    int64 nTimeExpires
+    string comment
+
+ckey : enCrypted key continues with vector<unsigned char> that is public key
+  value is vector<unsigned char> that is private key (32 byte private part,
+    not full 271-byte OpenSSL representation)
+
+mkey : master key continues with unsigned int that is ID of the master key
+  value is serialized CMasterKey:
+    vector<unsigned char> encrypted key
+    vector<unsigned char> encrypted salt
+    unsigned int indicating the method to derive key from password
+    unsigned int indicating the number of times the derivation algorithm should be iterated
+    vector<unsigned char> holding other parameters to the derivation algorithm
+
+defaultkey :
+  value is vector<unsigned char> default key
+
+version :
+  value is int version number
+
+setting : key continues with setting name, value depends on setting as follows:
+  addrIncoming : CAddress serialized
+  addrProxy : CAddress serialized
+  fGenerateBitcoins : 1-character (boolean)
+  fLimitProcessors : 1-character (boolean)
+  fMinimizeOnClose : 1-character (boolean)
+  fMinimizeToTray : 1-character (boolean)
+  fUseProxy : 1-character (boolean)
+  nLimitProcessors : int
+  nTransactionFee : int64
+
+
+Complex value type serialization:
+---------------------------------
+
+CDiskTxPos:
+  uint32 nFile       : Which of the blk000*.dat files
+  uint32 nBlockPos   : Byte position in file
+  uint32 nTxPos      : Byte position in file
+
+CDiskBlockIndex:
+  int nVersion
+  uint256 hashNext
+  uint32 nFile       : Which of the blk000*.dat files
+  uint32 nBlockPos   : Byte position in file
+  int nHeight        : height in block chain
+  int blockVersion   :  ??? Not sure why version is repeated...
+  uint256 hashPrev
+  uint256 hashMerkleRoot
+  uint32 nTime
+  uint32 nBits
+  uint32 nNonce
+
+CBlock:
+    # Note:   4 bytes: f9 be b4 d9  are written before records in blk000N.dat files
+    # But the nBlockPos pointers in CDiskBlockIndex points to start of serializedSize
+  int serializedSize
+  int version
+  uint256 hashPrev
+  uint256 hashMerkleRoot
+  uint32 nTime
+  uint32 nBits
+  uint32 nNonce
+  vector<CTransaction>
+
+CAddress:
+  int nVersion
+  unsigned int nTime
+  uint64 nServices
+  unsigned char[12] pchReserved
+  unsigned int ip
+  unsigned short port
+
+CTransaction:
+  int nVersion
+  vector<CTxIn> vin
+  vector<CTxOut> vout
+  unsigned int nLockTime
+
+CTxIn:
+  COutPoint prevout
+  CScript scriptSig
+  unsigned int nSequence
+
+CTxOut:
+  int64 nValue
+  CScript scriptPubKey
+
+COutPoint:
+  36 bytes(FLATDATA): 32-byte hash, 4-byte unsigned int
+
+CScript:
+  vector<unsigned char> containing built-in little scripting-language script
+   (opcodes and arguments to do crypto stuff)
+
+CMerkleTx:
+  ... serialize CTransaction, then:
+  uint256 hashBlock
+  vector<uint256> vMerkleBranch
+  int nIndex
+
+CWalletTx:
+  ... serialized CMerkleTx, then:
+  vector<CMerkleTx> vtxPrev
+  map<string,string> mapValue
+  vector<pair<string,string> > vOrderForm
+  unsigned int fTimeReceivedIsTxTime
+  unsigned int nTimeReceived
+  char fFromMe
+  char fSpent
+
+CInv:
+  int type   :   1:tx   2:block
+  uint256 hash
+
+CBlockLocator:
+  int nVersion
+  vector<uint256>    # Block hashes, newest back to genesis block (dense to start, but then sparse)
+
+string:
+  1/3/5/9 bytes giving length:
+    1 byte if length < 253.
+    otherwise byte value '253'+ushort, '254'+uint, '255'+uint64
+  then length bytes.
+
+vector<type>:
+  1/3/5/9 bytes giving count (see string, above)
+  followed by that many <types> serialized one-after-another
+
+pair:
+  just first item followed by second item
+
+
+PUBLIC KEYS TO BITCOIN ADDRESSES
+--------------------------------
+
+Public key, in memory (65 bytes):
+
+0x94c7818:  0x04        0x57    0xcc    0xad    0xd7    0x1e    0xb0    0xf3
+0x94c7820:  0xc1        0x9d    0x22    0xb9    0xba    0x0e    0xa1    0xf3
+0x94c7828:  0x44        0x2a    0x6f    0x12    0x31    0x46    0xb5    0xbd
+0x94c7830:  0xff        0x10    0x60    0xbc    0xd1    0x11    0x68    0xe6
+0x94c7838:  0x6a        0x71    0xbe    0xd4    0xda    0x17    0x7c    0x12
+0x94c7840:  0xd7        0x30    0x9a    0xdd    0xfd    0xf5    0x6c    0x31
+0x94c7848:  0xd5        0xc8    0xa2    0x7b    0x8e    0x6a    0x22    0x20
+0x94c7850:  0x38        0x42    0xc6    0xc2    0x4f    0xd5    0x9b    0xd7
+0x94c7858:  0xb7
+Python string:
+public_key = "\x04\x57\xcc\xad\xd7\x1e\xb0\xf3\xc1\x9d\x22\xb9\xba\x0e\xa1\xf3\x44\x2a\x6f\x12\x31\x46\xb5\xbd\xff\x10\x60\xbc\xd1\x11\x68\xe6\x6a\x71\xbe\xd4\xda\x17\x7c\x12\xd7\x30\x9a\xdd\xfd\xf5\x6c\x31\xd5\xc8\xa2\x7b\x8e\x6a\x22\x20\x38\x42\xc6\xc2\x4f\xd5\x9b\xd7\xb7"
+
+SHA256 hash of that is:
+0xb6890938: 0x0d        0x72    0xab    0x02    0xc8    0xab    0x52    0xce
+0xb6890940: 0x7e        0x6b    0x04    0x00    0x95    0x58    0x09    0xf0
+0xb6890948: 0x93        0x48    0x21    0xb6    0x26    0xc3    0x27    0xc7
+0xb6890950: 0x9a        0x07    0x62    0xfd    0xbc    0x5e    0xb8    0xa5
+h1 = SHA256.new(public_key).digest()
+
+RIPEMD160(SHA256(public key)) is:
+0xb68909ac: 0x5c        0xc8    0x7f    0x4a    0x3f    0xdf    0xe3    0xa2
+0xb68909b4: 0x34        0x6b    0x69    0x53    0x26    0x7c    0xa8    0x67
+0xb68909bc: 0x28        0x26    0x30    0xd3
+h2 = RIPEMD160.new(h1).digest()
+
+Put version number '0x00' byte onto front:
+0x9eeb840:  0x00   0x5c   0xc8 0x7f 0x4a        0x3f    0xdf    0xe3
+0x9eeb848:  0xa2   0x34   0x6b 0x69 0x53        0x26    0x7c    0xa8
+0x9eeb850:  0x67   0x28   0x26 0x30 0xd3
+vh2 = "\x00"+h2
+
+Hash (double-SHA256) that:
+0xb68908e8:   0xf9   0xb7 0x8e  0x64    0x6e    0x20    0x27    0xc4
+0xb68908f0:   0xaa   0x62 0x66  0x04    0x2e    0xb6    0xa2    0xe0
+0xb68908f8:   0x41   0x03 0x9d  0xd8    0xe2    0x24    0x24    0xe8
+0xb6890900:   0x50   0xac 0x20  0x29    0xfb    0xcd    0xb4    0x6e
+h3=SHA256.new(SHA256.new(vh2).digest()).digest()
+
+Add first 4 bytes from Hash object onto end as check-bytes:
+0x9fa6628:  0x00  0x5c  0xc8    0x7f    0x4a    0x3f    0xdf    0xe3
+0x9fa6630:  0xa2  0x34  0x6b    0x69    0x53    0x26    0x7c    0xa8
+0x9fa6638:  0x67  0x28  0x26    0x30    0xd3    0xf9    0xb7    0x8e
+0x9fa6640:  0x64
+addr=vh2+h3[0:4]
+
+Result length should be:  int(math.floor(len(addr)*8/math.log(58,2)))
+Base58 encode that, front-pad with '1's if less than expected length
+to get:   19TbMSWwHvnxAKy12iNm3KdbGfzfaMFViT
+
+
+WIRE PROTOCOL NOTES
+-------------------
+Default port is 8333
+
+// Message format                                                                                                       
+//  (4) message start    { 0xf9, 0xbe, 0xb4, 0xd9 }
+//  (12) command
+//  (4) size       -- number of bytes of data
+//  (4) checksum   -- First four bytes of double SHA256 hash of data
+//  (x) data
+
+ --> messages might be split by network layer...
+
+Commands are:
+
+"version" :
+  int nVersion
+  uint64 nServices
+  int64 nTime
+  CAddress addrMe
+  CAddress addrFrom   # if nVersion > 106
+  uint64 nNonce
+  string strSubVer    # if nVersion > 106
+  int nStartingHeight # if nVersion > 209
+
+nNonce is random value (I think), used to detect self-connection.
+
+"verack" : no data.  Sent to sync min version (peer, peer)
+
+"addr" :
+  vector<CAddress>
+      (relayed to 10 random nodes so they spread)
+
+"inv" :
+  vector<CInv>
+
+"getdata" :   Asks for blocks or tx's (response is "block" or "tx" messages)
+  vector<CInv>
+
+"getblocks" :
+  CBLockLocator
+  uint256 hashStop
+
+"tx" :
+  CTransaction
+
+"block" :
+  CBlock
+
+"getaddr" : no data ("please send me addr messages")
+
+"checkorder"
+  uint256 hashReply
+  CWalletTx order
+
+"submitorder"
+  uint256 hashReply
+  CWalletTx wtxNew
+
+"reply"
+  uint256 hashReply
+
+"ping" : no data (and no reply)
+
+
+TRANSACTION SIGNING NOTES
+-------------------------
+
+So, I want to spend some bitcoin.
+First, I gotta have a Transaction to me-- specifically, a Transaction with a TxOut that contains a scriptPubKey that I can satisfy.
+
+TxOut.scriptPubKey's come in a couple different flavors:
+ DUP HASH160 {hash160(public_key) EQUALVERIFY CHECKSIG
+   --> I have to be able to supply a matching TxIn.scriptSig with: signature and public key
+ (public key) CHECKSIG
+   --> I have to supply a matching TxIn.scriptSig with: signature
+
+TODO: figure out what, exactly, is hashed (and how), and how, exactly, that hash value is signed with the private key.
+
+DIFFICULTY, NBITS, BN_mpi2bn
+----------------------------
+
+Hash targets are stored as "compact bignums;" the production block chain has an initial target of:
+0x1d00ffff  ... which means "create a bignum that is 0x1d (29) bytes long and has 0x00ffff as the
+three bytes on the big end."  Or: 0xffff0000000000000000000000000000000000000000000000000000
+
+As I write this, the current block->nBits is 0x1c010c5.  To compute difficulty:
+  php -r '$nBits = 0x1c010c5a; print( (0xffff << ((0x1d-($nBits>>24))*8)) / ($nBits&0xffffff) );'
+
+
diff --git a/bitcointools/README.txt b/bitcointools/README.txt
new file mode 100644 (file)
index 0000000..0939cf3
--- /dev/null
@@ -0,0 +1,42 @@
+----- dbdump.py -----
+Run    dbdump.py --help    for usage.  Database files are opened read-only, but
+you might want to backup your Bitcoin wallet.dat file just in case.
+
+You must quit Bitcoin before reading the transactions, blocks, or address database files.
+
+Requires the pycrypto library from  http://www.dlitz.net/software/pycrypto/
+to translate public keys into human-friendly Bitcoin addresses.
+
+Examples:
+
+Print out  wallet keys and transactions:
+  dbdump.py --wallet --wallet-tx
+
+Print out the "genesis block" (the very first block in the proof-of-work block chain):
+  dbdump.py --block=000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
+
+Print out one of the transactions from my wallet:
+  dbdump.py --transaction=c6e1bf883bceef0aa05113e189982055d9ba7212ddfc879798616a0d0828c98c
+  dbdump.py --transaction=c6e1...c98c
+
+Print out all 'received' transactions that aren't yet spent:
+  dbdump.py --wallet-tx-filter='fromMe:False.*spent:False'
+
+Print out all blocks involving transactions to the Bitcoin Faucet:
+  dbdump.py --search-blocks=15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC
+
+There's a special search term to look for non-standard transactions:
+  dbdump.py --search-blocks=NONSTANDARD_CSCRIPTS
+
+----- statistics.py -----
+Scan all the transactions in the block chain and dump out a .csv file that shows transaction volume per month.
+
+----- fixwallet.py -----
+Half-baked utility that reads a wallet.dat and writes out a new wallet.dat.
+
+Only half-baked because to be really useful I'd have to write serialize routines to re-pack data after modifying it...
+
+----- jsonToCSV.py -----
+Read JSON list-of-objects from standard input, writes CSV file to standard output.
+Useful for converting bitcoind's listtransactions output to CSV that can be
+imported into a spreadsheet.
diff --git a/bitcointools/__init__.py b/bitcointools/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bitcointools/address.py b/bitcointools/address.py
new file mode 100644 (file)
index 0000000..8fca690
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# Code for parsing the addr.dat file
+# NOTE: I think you have to shutdown the Bitcoin client to
+# successfully read addr.dat...
+#
+
+from bsddb.db import *
+import logging
+from operator import itemgetter
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address
+from util import short_hex
+from deserialize import *
+
+def dump_addresses(db_env):
+  db = DB(db_env)
+  try:
+    r = db.open("addr.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open addr.dat/main. Try quitting Bitcoin and running this again.")
+    sys.exit(1)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  for (key, value) in db.items():
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+
+    if type == "addr":
+      d = parse_CAddress(vds)
+      print(deserialize_CAddress(d))
+
+  db.close()
diff --git a/bitcointools/base58.py b/bitcointools/base58.py
new file mode 100644 (file)
index 0000000..0d034b8
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+"""encode/decode base58 in the same way that Bitcoin does"""
+
+import math
+
+__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+__b58base = len(__b58chars)
+
+def b58encode(v):
+  """ encode v, which is a string of bytes, to base58.    
+  """
+
+  long_value = 0L
+  for (i, c) in enumerate(v[::-1]):
+    long_value += (256**i) * ord(c)
+
+  result = ''
+  while long_value >= __b58base:
+    div, mod = divmod(long_value, __b58base)
+    result = __b58chars[mod] + result
+    long_value = div
+  result = __b58chars[long_value] + result
+
+  # Bitcoin does a little leading-zero-compression:
+  # leading 0-bytes in the input become leading-1s
+  nPad = 0
+  for c in v:
+    if c == '\0': nPad += 1
+    else: break
+
+  return (__b58chars[0]*nPad) + result
+
+def b58decode(v, length):
+  """ decode v into a string of len bytes
+  """
+  long_value = 0L
+  for (i, c) in enumerate(v[::-1]):
+    long_value += __b58chars.find(c) * (__b58base**i)
+
+  result = ''
+  while long_value >= 256:
+    div, mod = divmod(long_value, 256)
+    result = chr(mod) + result
+    long_value = div
+  result = chr(long_value) + result
+
+  nPad = 0
+  for c in v:
+    if c == __b58chars[0]: nPad += 1
+    else: break
+
+  result = chr(0)*nPad + result
+  if length is not None and len(result) != length:
+    return None
+
+  return result
+
+try:
+  # Python Crypto library is at: http://www.dlitz.net/software/pycrypto/
+  # Needed for RIPEMD160 hash function, used to compute
+  # Bitcoin addresses from internal public keys.
+  import Crypto.Hash.SHA256 as SHA256
+  import Crypto.Hash.RIPEMD160 as RIPEMD160
+  have_crypto = True
+except ImportError:
+  have_crypto = False
+
+def hash_160(public_key):
+  if not have_crypto:
+    return ''
+  h1 = SHA256.new(public_key).digest()
+  h2 = RIPEMD160.new(h1).digest()
+  return h2
+
+def public_key_to_bc_address(public_key):
+  if not have_crypto:
+    return ''
+  h160 = hash_160(public_key)
+  return hash_160_to_bc_address(h160)
+
+def hash_160_to_bc_address(h160):
+  if not have_crypto:
+    return ''
+  vh160 = "\x00"+h160  # \x00 is version 0
+  h3=SHA256.new(SHA256.new(vh160).digest()).digest()
+  addr=vh160+h3[0:4]
+  return b58encode(addr)
+
+def bc_address_to_hash_160(addr):
+  bytes = b58decode(addr, 25)
+  return bytes[1:21]
+
+if __name__ == '__main__':
+    x = '005cc87f4a3fdfe3a2346b6953267ca867282630d3f9b78e64'.decode('hex_codec')
+    encoded = b58encode(x)
+    print encoded, '19TbMSWwHvnxAKy12iNm3KdbGfzfaMFViT'
+    print b58decode(encoded, len(x)).encode('hex_codec'), x.encode('hex_codec')
diff --git a/bitcointools/blkindex.py b/bitcointools/blkindex.py
new file mode 100644 (file)
index 0000000..faf1113
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# Code for parsing the blkindex.dat file
+#
+
+from bsddb.db import *
+import logging
+from operator import itemgetter
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address
+from util import short_hex
+from deserialize import *
+
+def dump_blkindex_summary(db_env):
+  db = DB(db_env)
+  try:
+    r = db.open("blkindex.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open blkindex.dat/main.  Try quitting any running Bitcoin apps.")
+    sys.exit(1)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  n_tx = 0
+  n_blockindex = 0
+
+  print("blkindex file summary:")
+  for (key, value) in db.items():
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+
+    if type == "tx":
+      n_tx += 1
+    elif type == "blockindex":
+      n_blockindex += 1
+    elif type == "version":
+      version = vds.read_int32()
+      print(" Version: %d"%(version,))
+    elif type == "hashBestChain":
+      hash = vds.read_bytes(32)
+      print(" HashBestChain: %s"%(hash.encode('hex_codec'),))
+    else:
+      logging.warn("blkindex: unknown type '%s'"%(type,))
+      continue
+
+  print(" %d transactions, %d blocks."%(n_tx, n_blockindex))
+  db.close()
diff --git a/bitcointools/block.py b/bitcointools/block.py
new file mode 100644 (file)
index 0000000..bff0744
--- /dev/null
@@ -0,0 +1,240 @@
+#
+# Code for dumping a single block, given its ID (hash)
+#
+
+from bsddb.db import *
+import logging
+import os.path
+import re
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address
+from util import short_hex, long_hex
+from deserialize import *
+
+def _open_blkindex(db_env):
+  db = DB(db_env)
+  try:
+    r = db.open("blkindex.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
+  except DBError:
+    r = True
+  if r is not None:
+    logging.error("Couldn't open blkindex.dat/main.  Try quitting any running Bitcoin apps.")
+    sys.exit(1)
+  return db
+
+def _read_CDiskTxPos(stream):
+  n_file = stream.read_uint32()
+  n_block_pos = stream.read_uint32()
+  n_tx_pos = stream.read_uint32()
+  return (n_file, n_block_pos, n_tx_pos)
+
+def _dump_block(datadir, nFile, nBlockPos, hash256, hashNext, do_print=True):
+  blockfile = open(os.path.join(datadir, "blk%04d.dat"%(nFile,)), "rb")
+  ds = BCDataStream()
+  ds.map_file(blockfile, nBlockPos)
+  d = parse_Block(ds)
+  block_string = deserialize_Block(d)
+  ds.close_file()
+  blockfile.close()
+  if do_print:
+    print "BLOCK "+long_hex(hash256[::-1])
+    print "Next block: "+long_hex(hashNext[::-1])
+    print block_string
+  return block_string
+
+def _parse_block_index(vds):
+  d = {}
+  d['version'] = vds.read_int32()
+  d['hashNext'] = vds.read_bytes(32)
+  d['nFile'] = vds.read_uint32()
+  d['nBlockPos'] = vds.read_uint32()
+  d['nHeight'] = vds.read_int32()
+
+  header_start = vds.read_cursor
+  d['b_version'] = vds.read_int32()
+  d['hashPrev'] = vds.read_bytes(32)
+  d['hashMerkle'] = vds.read_bytes(32)
+  d['nTime'] = vds.read_int32()
+  d['nBits'] = vds.read_int32()
+  d['nNonce'] = vds.read_int32()
+  header_end = vds.read_cursor
+  d['__header__'] = vds.input[header_start:header_end]
+  return d
+
+def dump_block(datadir, db_env, block_hash):
+  """ Dump a block, given hexadecimal hash-- either the full hash
+      OR a short_hex version of the it.
+  """
+  db = _open_blkindex(db_env)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  n_blockindex = 0
+
+  key_prefix = "\x0ablockindex"
+  cursor = db.cursor()
+  (key, value) = cursor.set_range(key_prefix)
+
+  while key.startswith(key_prefix):
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+    hash256 = kds.read_bytes(32)
+    hash_hex = long_hex(hash256[::-1])
+    block_data = _parse_block_index(vds)
+
+    if (hash_hex.startswith(block_hash) or short_hex(hash256[::-1]).startswith(block_hash)):
+      print "Block height: "+str(block_data['nHeight'])
+      _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'], hash256, block_data['hashNext'])
+
+    (key, value) = cursor.next()
+
+  db.close()
+
+def read_block(db_cursor, hash):
+  (key,value) = db_cursor.set_range("\x0ablockindex"+hash)
+  vds = BCDataStream()
+  vds.clear(); vds.write(value)
+  block_data = _parse_block_index(vds)
+  block_data['hash256'] = hash
+  return block_data
+
+def scan_blocks(datadir, db_env, callback_fn):
+  """ Scan through blocks, from last through genesis block,
+      calling callback_fn(block_data) for each.
+      callback_fn should return False if scanning should
+      stop, True if it should continue.
+      Returns last block_data scanned.
+  """
+  db = _open_blkindex(db_env)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+  
+  # Read the hashBestChain record:
+  cursor = db.cursor()
+  (key, value) = cursor.set_range("\x0dhashBestChain")
+  vds.write(value)
+  hashBestChain = vds.read_bytes(32)
+
+  block_data = read_block(cursor, hashBestChain)
+
+  while callback_fn(block_data):
+    if block_data['nHeight'] == 0:
+      break;
+    block_data = read_block(cursor, block_data['hashPrev'])
+  return block_data
+
+
+def dump_block_n(datadir, db_env, block_number):
+  """ Dump a block given block number (== height, genesis block is 0)
+  """
+  def scan_callback(block_data):
+    return not block_data['nHeight'] == block_number
+
+  block_data = scan_blocks(datadir, db_env, scan_callback)
+
+  print "Block height: "+str(block_data['nHeight'])
+  _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'], block_data['hash256'], block_data['hashNext'])
+
+def search_blocks(datadir, db_env, pattern):
+  """ Dump a block given block number (== height, genesis block is 0)
+  """
+  db = _open_blkindex(db_env)
+  kds = BCDataStream()
+  vds = BCDataStream()
+  
+  # Read the hashBestChain record:
+  cursor = db.cursor()
+  (key, value) = cursor.set_range("\x0dhashBestChain")
+  vds.write(value)
+  hashBestChain = vds.read_bytes(32)
+  block_data = read_block(cursor, hashBestChain)
+
+  if pattern == "NONSTANDARD_CSCRIPTS": # Hack to look for non-standard transactions
+    search_odd_scripts(datadir, cursor, block_data)
+    return
+
+  while True:
+    block_string = _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'],
+                               block_data['hash256'], block_data['hashNext'], False)
+    
+    if re.search(pattern, block_string) is not None:
+      print "MATCH: Block height: "+str(block_data['nHeight'])
+      print block_string
+
+    if block_data['nHeight'] == 0:
+      break
+    block_data = read_block(cursor, block_data['hashPrev'])
+    
+def search_odd_scripts(datadir, cursor, block_data):
+  """ Look for non-standard transactions """
+  while True:
+    block_string = _dump_block(datadir, block_data['nFile'], block_data['nBlockPos'],
+                               block_data['hash256'], block_data['hashNext'], False)
+    
+    found_nonstandard = False
+    for m in re.finditer(r'TxIn:(.*?)$', block_string, re.MULTILINE):
+      s = m.group(1)
+      if re.match(r'\s*COIN GENERATED coinbase:\w+$', s): continue
+      if re.match(r'.*sig: \d+:\w+...\w+ \d+:\w+...\w+$', s): continue
+      if re.match(r'.*sig: \d+:\w+...\w+$', s): continue
+      print "Nonstandard TxIn: "+s
+      found_nonstandard = True
+      break
+
+    for m in re.finditer(r'TxOut:(.*?)$', block_string, re.MULTILINE):
+      s = m.group(1)
+      if re.match(r'.*Script: DUP HASH160 \d+:\w+...\w+ EQUALVERIFY CHECKSIG$', s): continue
+      if re.match(r'.*Script: \d+:\w+...\w+ CHECKSIG$', s): continue
+      print "Nonstandard TxOut: "+s
+      found_nonstandard = True
+      break
+
+    if found_nonstandard:
+      print "NONSTANDARD TXN: Block height: "+str(block_data['nHeight'])
+      print block_string
+
+    if block_data['nHeight'] == 0:
+      break
+    block_data = read_block(cursor, block_data['hashPrev'])
+  
+def check_block_chain(db_env):
+  """ Make sure hashPrev/hashNext pointers are consistent through block chain """
+  db = _open_blkindex(db_env)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+  
+  # Read the hashBestChain record:
+  cursor = db.cursor()
+  (key, value) = cursor.set_range("\x0dhashBestChain")
+  vds.write(value)
+  hashBestChain = vds.read_bytes(32)
+
+  back_blocks = []
+
+  block_data = read_block(cursor, hashBestChain)
+
+  while block_data['nHeight'] > 0:
+    back_blocks.append( (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext']) )
+    block_data = read_block(cursor, block_data['hashPrev'])
+
+  back_blocks.append( (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext']) )
+  genesis_block = block_data
+  
+  print("check block chain: genesis block merkle hash is: %s"%(block_data['hashMerkle'][::-1].encode('hex_codec')))
+
+  while block_data['hashNext'] != ('\0'*32):
+    forward = (block_data['nHeight'], block_data['hashMerkle'], block_data['hashPrev'], block_data['hashNext'])
+    back = back_blocks.pop()
+    if forward != back:
+      print("Forward/back block mismatch at height %d!"%(block_data['nHeight'],))
+      print(" Forward: "+str(forward))
+      print(" Back: "+str(back))
+    block_data = read_block(cursor, block_data['hashNext'])
diff --git a/bitcointools/blocks.py b/bitcointools/blocks.py
new file mode 100644 (file)
index 0000000..8384751
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# Code for parsing the blkindex.dat file
+#
+
+from bsddb.db import *
+import logging
+from operator import itemgetter
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address
+from util import short_hex
+from deserialize import *
+
+def _read_CDiskTxPos(stream):
+  n_file = stream.read_uint32()
+  n_block_pos = stream.read_uint32()
+  n_tx_pos = stream.read_uint32()
+  return (n_file, n_block_pos, n_tx_pos)
+
+def dump_blockindex(db_env, owner=None, n_to_dump=1000):
+  db = DB(db_env)
+  r = db.open("blkindex.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
+  if r is not None:
+    logging.error("Couldn't open blkindex.dat/main")
+    sys.exit(1)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  wallet_transactions = []
+
+  for (i, (key, value)) in enumerate(db.items()):
+    if i > n_to_dump:
+      break
+
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+
+    if type == "tx":
+      hash256 = kds.read_bytes(32)
+      version = vds.read_uint32()
+      tx_pos = _read_CDiskTxPos(vds)
+      print("Tx(%s:%d %d %d)"%((short_hex(hash256),)+tx_pos))
+      n_tx_out = vds.read_compact_size()
+      for i in range(0,n_tx_out):
+        tx_out = _read_CDiskTxPos(vds)
+        if tx_out[0] != 0xffffffffL:  # UINT_MAX means no TxOuts (unspent)
+          print("  ==> TxOut(%d %d %d)"%tx_out)
+      
+    else:
+      logging.warn("blkindex: type %s"%(type,))
+      continue
+
+  db.close()
+
diff --git a/bitcointools/dbdump.py b/bitcointools/dbdump.py
new file mode 100755 (executable)
index 0000000..79340da
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Code for dumping the bitcoin Berkeley db files in a human-readable format
+#
+from bsddb.db import *
+import logging
+import sys
+
+from address import dump_addresses
+from wallet import dump_wallet, dump_accounts
+from blkindex import dump_blkindex_summary
+from transaction import dump_transaction
+from block import dump_block, dump_block_n, search_blocks, check_block_chain
+from util import determine_db_dir, create_env
+
+def main():
+  import optparse
+  parser = optparse.OptionParser(usage="%prog [options]")
+  parser.add_option("--datadir", dest="datadir", default=None,
+                    help="Look for files here (defaults to bitcoin default)")
+  parser.add_option("--wallet", action="store_true", dest="dump_wallet", default=False,
+                    help="Print out contents of the wallet.dat file")
+  parser.add_option("--wallet-tx", action="store_true", dest="dump_wallet_tx", default=False,
+                    help="Print transactions in the wallet.dat file")
+  parser.add_option("--wallet-tx-filter", action="store", dest="wallet_tx_filter", default="",
+                    help="Only print transactions that match given string/regular expression")
+  parser.add_option("--accounts", action="store_true", dest="dump_accounts", default="",
+                    help="Print out account names, one per line")
+  parser.add_option("--blkindex", action="store_true", dest="dump_blkindex", default=False,
+                    help="Print out summary of blkindex.dat file")
+  parser.add_option("--check-block-chain", action="store_true", dest="check_chain", default=False,
+                    help="Scan back and forward through the block chain, looking for inconsistencies")
+  parser.add_option("--address", action="store_true", dest="dump_addr", default=False,
+                    help="Print addresses in the addr.dat file")
+  parser.add_option("--transaction", action="store", dest="dump_transaction", default=None,
+                    help="Dump a single transaction, given hex transaction id (or abbreviated id)")
+  parser.add_option("--block", action="store", dest="dump_block", default=None,
+                    help="Dump a single block, given its hex hash (or abbreviated hex hash) OR block height")
+  parser.add_option("--search-blocks", action="store", dest="search_blocks", default=None,
+                    help="Search the block chain for blocks containing given regex pattern")
+  (options, args) = parser.parse_args()
+
+  if options.datadir is None:
+    db_dir = determine_db_dir()
+  else:
+    db_dir = options.datadir
+
+  try:
+    db_env = create_env(db_dir)
+  except DBNoSuchFileError:
+    logging.error("Couldn't open " + db_dir)
+    sys.exit(1)
+
+  dump_tx = options.dump_wallet_tx
+  if len(options.wallet_tx_filter) > 0:
+    dump_tx = True
+  if options.dump_wallet or dump_tx:
+    dump_wallet(db_env, options.dump_wallet, dump_tx, options.wallet_tx_filter)
+  if options.dump_accounts:
+    dump_accounts(db_env)
+
+  if options.dump_addr:
+    dump_addresses(db_env)
+
+  if options.check_chain:
+    check_block_chain(db_env)
+
+  if options.dump_blkindex:
+    dump_blkindex_summary(db_env)
+
+  if options.dump_transaction is not None:
+    dump_transaction(db_dir, db_env, options.dump_transaction)
+
+  if options.dump_block is not None:
+    if len(options.dump_block) < 7: # Probably an integer...
+      try:
+        dump_block_n(db_dir, db_env, int(options.dump_block))
+      except ValueError:
+        dump_block(db_dir, db_env, options.dump_block)
+    else:
+      dump_block(db_dir, db_env, options.dump_block)
+
+  if options.search_blocks is not None:
+    search_blocks(db_dir, db_env, options.search_blocks)
+
+  db_env.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/bitcointools/deserialize.py b/bitcointools/deserialize.py
new file mode 100644 (file)
index 0000000..a1764da
--- /dev/null
@@ -0,0 +1,303 @@
+#
+#
+#
+
+from BCDataStream import *
+from enumeration import Enumeration
+from base58 import public_key_to_bc_address, hash_160_to_bc_address
+import socket
+import time
+from util import short_hex, long_hex
+
+def parse_CAddress(vds):
+  d = {}
+  d['nVersion'] = vds.read_int32()
+  d['nTime'] = vds.read_uint32()
+  d['nServices'] = vds.read_uint64()
+  d['pchReserved'] = vds.read_bytes(12)
+  d['ip'] = socket.inet_ntoa(vds.read_bytes(4))
+  d['port'] = vds.read_uint16()
+  return d
+
+def deserialize_CAddress(d):
+  return d['ip']+":"+str(d['port'])+" (lastseen: %s)"%(time.ctime(d['nTime']),)
+
+def parse_setting(setting, vds):
+  if setting[0] == "f":  # flag (boolean) settings
+    return str(vds.read_boolean())
+  elif setting == "addrIncoming":
+    return "" # bitcoin 0.4 purposely breaks addrIncoming setting in encrypted wallets.
+  elif setting[0:4] == "addr": # CAddress
+    d = parse_CAddress(vds)
+    return deserialize_CAddress(d)
+  elif setting == "nTransactionFee":
+    return vds.read_int64()
+  elif setting == "nLimitProcessors":
+    return vds.read_int32()
+  return 'unknown setting'
+
+def parse_TxIn(vds):
+  d = {}
+  d['prevout_hash'] = vds.read_bytes(32)
+  d['prevout_n'] = vds.read_uint32()
+  d['scriptSig'] = vds.read_bytes(vds.read_compact_size())
+  d['sequence'] = vds.read_uint32()
+  return d
+def deserialize_TxIn(d, transaction_index=None, owner_keys=None):
+  if d['prevout_hash'] == "\x00"*32:
+    result = "TxIn: COIN GENERATED"
+    result += " coinbase:"+d['scriptSig'].encode('hex_codec')
+  elif transaction_index is not None and d['prevout_hash'] in transaction_index:
+    p = transaction_index[d['prevout_hash']]['txOut'][d['prevout_n']]
+    result = "TxIn: value: %f"%(p['value']/1.0e8,)
+    result += " prev("+long_hex(d['prevout_hash'][::-1])+":"+str(d['prevout_n'])+")"
+  else:
+    result = "TxIn: prev("+long_hex(d['prevout_hash'][::-1])+":"+str(d['prevout_n'])+")"
+    pk = extract_public_key(d['scriptSig'])
+    result += " pubkey: "+pk
+    result += " sig: "+decode_script(d['scriptSig'])
+  if d['sequence'] < 0xffffffff: result += " sequence: "+hex(d['sequence'])
+  return result
+
+def parse_TxOut(vds):
+  d = {}
+  d['value'] = vds.read_int64()
+  d['scriptPubKey'] = vds.read_bytes(vds.read_compact_size())
+  return d
+
+def deserialize_TxOut(d, owner_keys=None):
+  result =  "TxOut: value: %f"%(d['value']/1.0e8,)
+  pk = extract_public_key(d['scriptPubKey'])
+  result += " pubkey: "+pk
+  result += " Script: "+decode_script(d['scriptPubKey'])
+  if owner_keys is not None:
+    if pk in owner_keys: result += " Own: True"
+    else: result += " Own: False"
+  return result
+
+def parse_Transaction(vds):
+  d = {}
+  d['version'] = vds.read_int32()
+  n_vin = vds.read_compact_size()
+  d['txIn'] = []
+  for i in xrange(n_vin):
+    d['txIn'].append(parse_TxIn(vds))
+  n_vout = vds.read_compact_size()
+  d['txOut'] = []
+  for i in xrange(n_vout):
+    d['txOut'].append(parse_TxOut(vds))
+  d['lockTime'] = vds.read_uint32()
+  return d
+def deserialize_Transaction(d, transaction_index=None, owner_keys=None):
+  result = "%d tx in, %d out\n"%(len(d['txIn']), len(d['txOut']))
+  for txIn in d['txIn']:
+    result += deserialize_TxIn(txIn, transaction_index) + "\n"
+  for txOut in d['txOut']:
+    result += deserialize_TxOut(txOut, owner_keys) + "\n"
+  return result
+
+def parse_MerkleTx(vds):
+  d = parse_Transaction(vds)
+  d['hashBlock'] = vds.read_bytes(32)
+  n_merkleBranch = vds.read_compact_size()
+  d['merkleBranch'] = vds.read_bytes(32*n_merkleBranch)
+  d['nIndex'] = vds.read_int32()
+  return d
+
+def deserialize_MerkleTx(d, transaction_index=None, owner_keys=None):
+  tx = deserialize_Transaction(d, transaction_index, owner_keys)
+  result = "block: "+(d['hashBlock'][::-1]).encode('hex_codec')
+  result += " %d hashes in merkle branch\n"%(len(d['merkleBranch'])/32,)
+  return result+tx
+
+def parse_WalletTx(vds):
+  d = parse_MerkleTx(vds)
+  n_vtxPrev = vds.read_compact_size()
+  d['vtxPrev'] = []
+  for i in xrange(n_vtxPrev):
+    d['vtxPrev'].append(parse_MerkleTx(vds))
+
+  d['mapValue'] = {}
+  n_mapValue = vds.read_compact_size()
+  for i in xrange(n_mapValue):
+    key = vds.read_string()
+    value = vds.read_string()
+    d['mapValue'][key] = value
+  n_orderForm = vds.read_compact_size()
+  d['orderForm'] = []
+  for i in xrange(n_orderForm):
+    first = vds.read_string()
+    second = vds.read_string()
+    d['orderForm'].append( (first, second) )
+  d['fTimeReceivedIsTxTime'] = vds.read_uint32()
+  d['timeReceived'] = vds.read_uint32()
+  d['fromMe'] = vds.read_boolean()
+  d['spent'] = vds.read_boolean()
+
+  return d
+
+def deserialize_WalletTx(d, transaction_index=None, owner_keys=None):
+  result = deserialize_MerkleTx(d, transaction_index, owner_keys)
+  result += "%d vtxPrev txns\n"%(len(d['vtxPrev']),)
+  result += "mapValue:"+str(d['mapValue'])
+  if len(d['orderForm']) > 0:
+    result += "\n"+" orderForm:"+str(d['orderForm'])
+  result += "\n"+"timeReceived:"+time.ctime(d['timeReceived'])
+  result += " fromMe:"+str(d['fromMe'])+" spent:"+str(d['spent'])
+  return result
+
+# The CAuxPow (auxiliary proof of work) structure supports merged mining.
+# A flag in the block version field indicates the structure's presence.
+# As of 8/2011, the Original Bitcoin Client does not use it.  CAuxPow
+# originated in Namecoin; see
+# https://github.com/vinced/namecoin/blob/mergedmine/doc/README_merged-mining.md.
+def parse_AuxPow(vds):
+  d = parse_MerkleTx(vds)
+  n_chainMerkleBranch = vds.read_compact_size()
+  d['chainMerkleBranch'] = vds.read_bytes(32*n_chainMerkleBranch)
+  d['chainIndex'] = vds.read_int32()
+  d['parentBlock'] = parse_BlockHeader(vds)
+  return d
+
+def parse_BlockHeader(vds):
+  d = {}
+  header_start = vds.read_cursor
+  d['version'] = vds.read_int32()
+  d['hashPrev'] = vds.read_bytes(32)
+  d['hashMerkleRoot'] = vds.read_bytes(32)
+  d['nTime'] = vds.read_uint32()
+  d['nBits'] = vds.read_uint32()
+  d['nNonce'] = vds.read_uint32()
+  header_end = vds.read_cursor
+  d['__header__'] = vds.input[header_start:header_end]
+  return d
+
+def parse_Block(vds):
+  d = parse_BlockHeader(vds)
+  if d['version'] & (1 << 8):
+    d['auxpow'] = parse_AuxPow(vds)
+  d['transactions'] = []
+  nTransactions = vds.read_compact_size()
+  for i in xrange(nTransactions):
+    d['transactions'].append(parse_Transaction(vds))
+
+  return d
+  
+def deserialize_Block(d):
+  result = "Time: "+time.ctime(d['nTime'])+" Nonce: "+str(d['nNonce'])
+  result += "\nnBits: 0x"+hex(d['nBits'])
+  result += "\nhashMerkleRoot: 0x"+d['hashMerkleRoot'][::-1].encode('hex_codec')
+  result += "\nPrevious block: "+d['hashPrev'][::-1].encode('hex_codec')
+  result += "\n%d transactions:\n"%len(d['transactions'])
+  for t in d['transactions']:
+    result += deserialize_Transaction(t)+"\n"
+  result += "\nRaw block header: "+d['__header__'].encode('hex_codec')
+  return result
+
+def parse_BlockLocator(vds):
+  d = { 'hashes' : [] }
+  nHashes = vds.read_compact_size()
+  for i in xrange(nHashes):
+    d['hashes'].append(vds.read_bytes(32))
+  return d
+
+def deserialize_BlockLocator(d):
+  result = "Block Locator top: "+d['hashes'][0][::-1].encode('hex_codec')
+  return result
+
+opcodes = Enumeration("Opcodes", [
+    ("OP_0", 0), ("OP_PUSHDATA1",76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED",
+    "OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7",
+    "OP_8", "OP_9", "OP_10", "OP_11", "OP_12", "OP_13", "OP_14", "OP_15", "OP_16",
+    "OP_NOP", "OP_VER", "OP_IF", "OP_NOTIF", "OP_VERIF", "OP_VERNOTIF", "OP_ELSE", "OP_ENDIF", "OP_VERIFY",
+    "OP_RETURN", "OP_TOALTSTACK", "OP_FROMALTSTACK", "OP_2DROP", "OP_2DUP", "OP_3DUP", "OP_2OVER", "OP_2ROT", "OP_2SWAP",
+    "OP_IFDUP", "OP_DEPTH", "OP_DROP", "OP_DUP", "OP_NIP", "OP_OVER", "OP_PICK", "OP_ROLL", "OP_ROT",
+    "OP_SWAP", "OP_TUCK", "OP_CAT", "OP_SUBSTR", "OP_LEFT", "OP_RIGHT", "OP_SIZE", "OP_INVERT", "OP_AND",
+    "OP_OR", "OP_XOR", "OP_EQUAL", "OP_EQUALVERIFY", "OP_RESERVED1", "OP_RESERVED2", "OP_1ADD", "OP_1SUB", "OP_2MUL",
+    "OP_2DIV", "OP_NEGATE", "OP_ABS", "OP_NOT", "OP_0NOTEQUAL", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV",
+    "OP_MOD", "OP_LSHIFT", "OP_RSHIFT", "OP_BOOLAND", "OP_BOOLOR",
+    "OP_NUMEQUAL", "OP_NUMEQUALVERIFY", "OP_NUMNOTEQUAL", "OP_LESSTHAN",
+    "OP_GREATERTHAN", "OP_LESSTHANOREQUAL", "OP_GREATERTHANOREQUAL", "OP_MIN", "OP_MAX",
+    "OP_WITHIN", "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160",
+    "OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG",
+    "OP_CHECKMULTISIGVERIFY",
+    ("OP_SINGLEBYTE_END", 0xF0),
+    ("OP_DOUBLEBYTE_BEGIN", 0xF000),
+    "OP_PUBKEY", "OP_PUBKEYHASH",
+    ("OP_INVALIDOPCODE", 0xFFFF),
+])
+
+def script_GetOp(bytes):
+  i = 0
+  while i < len(bytes):
+    vch = None
+    opcode = ord(bytes[i])
+    i += 1
+    if opcode >= opcodes.OP_SINGLEBYTE_END:
+      opcode <<= 8
+      opcode |= bytes[i]
+      i += 1
+
+    if opcode <= opcodes.OP_PUSHDATA4:
+      nSize = opcode
+      if opcode == opcodes.OP_PUSHDATA1:
+        nSize = ord(bytes[i])
+        i += 1
+      elif opcode == opcodes.OP_PUSHDATA2:
+        nSize = unpack_from('<H', bytes, i)
+        i += 2
+      elif opcode == opcodes.OP_PUSHDATA4:
+        nSize = unpack_from('<I', bytes, i)
+        i += 4
+      vch = bytes[i:i+nSize]
+      i += nSize
+
+    yield (opcode, vch)
+
+def script_GetOpName(opcode):
+  return (opcodes.whatis(opcode)).replace("OP_", "")
+
+def decode_script(bytes):
+  result = ''
+  for (opcode, vch) in script_GetOp(bytes):
+    if len(result) > 0: result += " "
+    if opcode <= opcodes.OP_PUSHDATA4:
+      result += "%d:"%(opcode,)
+      result += short_hex(vch)
+    else:
+      result += script_GetOpName(opcode)
+  return result
+
+def match_decoded(decoded, to_match):
+  if len(decoded) != len(to_match):
+    return False;
+  for i in range(len(decoded)):
+    if to_match[i] == opcodes.OP_PUSHDATA4 and decoded[i][0] <= opcodes.OP_PUSHDATA4:
+      continue  # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent.
+    if to_match[i] != decoded[i][0]:
+      return False
+  return True
+
+def extract_public_key(bytes):
+  decoded = [ x for x in script_GetOp(bytes) ]
+
+  # non-generated TxIn transactions push a signature
+  # (seventy-something bytes) and then their public key
+  # (65 bytes) onto the stack:
+  match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ]
+  if match_decoded(decoded, match):
+    return public_key_to_bc_address(decoded[1][1])
+
+  # The Genesis Block, self-payments, and pay-by-IP-address payments look like:
+  # 65 BYTES:... CHECKSIG
+  match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ]
+  if match_decoded(decoded, match):
+    return public_key_to_bc_address(decoded[0][1])
+
+  # Pay-by-Bitcoin-address TxOuts look like:
+  # DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG
+  match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ]
+  if match_decoded(decoded, match):
+    return hash_160_to_bc_address(decoded[2][1])
+
+  return "(None)"
diff --git a/bitcointools/enumeration.py b/bitcointools/enumeration.py
new file mode 100644 (file)
index 0000000..d3d2612
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# enum-like type
+# From the Python Cookbook, downloaded from http://code.activestate.com/recipes/67107/
+#
+import types, string, exceptions
+
+class EnumException(exceptions.Exception):
+    pass
+
+class Enumeration:
+    def __init__(self, name, enumList):
+        self.__doc__ = name
+        lookup = { }
+        reverseLookup = { }
+        i = 0
+        uniqueNames = [ ]
+        uniqueValues = [ ]
+        for x in enumList:
+            if type(x) == types.TupleType:
+                x, i = x
+            if type(x) != types.StringType:
+                raise EnumException, "enum name is not a string: " + x
+            if type(i) != types.IntType:
+                raise EnumException, "enum value is not an integer: " + i
+            if x in uniqueNames:
+                raise EnumException, "enum name is not unique: " + x
+            if i in uniqueValues:
+                raise EnumException, "enum value is not unique for " + x
+            uniqueNames.append(x)
+            uniqueValues.append(i)
+            lookup[x] = i
+            reverseLookup[i] = x
+            i = i + 1
+        self.lookup = lookup
+        self.reverseLookup = reverseLookup
+    def __getattr__(self, attr):
+        if not self.lookup.has_key(attr):
+            raise AttributeError
+        return self.lookup[attr]
+    def whatis(self, value):
+        return self.reverseLookup[value]
diff --git a/bitcointools/fixwallet.py b/bitcointools/fixwallet.py
new file mode 100755 (executable)
index 0000000..dda57a4
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# Recover from a semi-corrupt wallet
+#
+from bsddb.db import *
+import logging
+import sys
+
+from wallet import rewrite_wallet, trim_wallet
+from util import determine_db_dir, create_env
+
+def main():
+  import optparse
+  parser = optparse.OptionParser(usage="%prog [options]")
+  parser.add_option("--datadir", dest="datadir", default=None,
+                    help="Look for files here (defaults to bitcoin default)")
+  parser.add_option("--out", dest="outfile", default="walletNEW.dat",
+                    help="Name of output file (default: walletNEW.dat)")
+  parser.add_option("--clean", action="store_true", dest="clean", default=False,
+                    help="Clean out old, spent change addresses and transactions")
+  parser.add_option("--skipkey", dest="skipkey",
+                    help="Skip entries with keys that contain given string")
+  parser.add_option("--tweakspent", dest="tweakspent",
+                    help="Tweak transaction to mark unspent")
+  (options, args) = parser.parse_args()
+
+  if options.datadir is None:
+    db_dir = determine_db_dir()
+  else:
+    db_dir = options.datadir
+
+  try:
+    db_env = create_env(db_dir)
+  except DBNoSuchFileError:
+    logging.error("Couldn't open " + db_dir)
+    sys.exit(1)
+
+  if options.clean:
+    trim_wallet(db_env, options.outfile)
+
+  elif options.skipkey:
+    def pre_put_callback(type, data):
+      if options.skipkey in data['__key__']:
+        return False
+      return True
+    rewrite_wallet(db_env, options.outfile, pre_put_callback)
+  elif options.tweakspent:
+    txid = options.tweakspent.decode('hex_codec')[::-1]
+    def tweak_spent_callback(type, data):
+      if txid in data['__key__']:
+        data['__value__'] = data['__value__'][:-1]+'\0'
+      return True
+    rewrite_wallet(db_env, options.outfile, tweak_spent_callback)
+    pass
+  else:
+    rewrite_wallet(db_env, options.outfile)
+
+  db_env.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/bitcointools/jsonToCSV.py b/bitcointools/jsonToCSV.py
new file mode 100755 (executable)
index 0000000..a41ee84
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+#
+# Reads an array of JSON objects and writes out CSV-format,
+# with key names in first row.
+# Columns will be union of all keys in the objects.
+#
+
+import csv
+import json
+import sys
+
+json_string = sys.stdin.read()
+json_array = json.loads(json_string)
+
+columns = set()
+for item in json_array:
+  columns.update(set(item))
+
+writer = csv.writer(sys.stdout)
+writer.writerow(list(columns))
+for item in json_array:
+  row = []
+  for c in columns:
+    if c in item: row.append(str(item[c]))
+    else: row.append('')
+  writer.writerow(row)
+  
diff --git a/bitcointools/statistics.py b/bitcointools/statistics.py
new file mode 100755 (executable)
index 0000000..fd2c01f
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Read the block database, generate monthly statistics and dump out
+# a CSV file.
+#
+from bsddb.db import *
+from datetime import date
+import logging
+import os
+import sys
+
+from BCDataStream import *
+from block import scan_blocks
+from collections import defaultdict
+from deserialize import parse_Block
+from util import determine_db_dir, create_env
+
+def main():
+  import optparse
+  parser = optparse.OptionParser(usage="%prog [options]")
+  parser.add_option("--datadir", dest="datadir", default=None,
+                    help="Look for files here (defaults to bitcoin default)")
+  (options, args) = parser.parse_args()
+
+  if options.datadir is None:
+    db_dir = determine_db_dir()
+  else:
+    db_dir = options.datadir
+
+  try:
+    db_env = create_env(db_dir)
+  except DBNoSuchFileError:
+    logging.error("Couldn't open " + db_dir)
+    sys.exit(1)
+
+  blockfile = open(os.path.join(db_dir, "blk%04d.dat"%(1,)), "rb")
+  block_datastream = BCDataStream()
+  block_datastream.map_file(blockfile, 0)
+
+  n_transactions = defaultdict(int)
+  v_transactions = defaultdict(float)
+  v_transactions_min = defaultdict(float)
+  v_transactions_max = defaultdict(float)
+  def gather_stats(block_data):
+    block_datastream.seek_file(block_data['nBlockPos'])
+    data = parse_Block(block_datastream)
+    block_date = date.fromtimestamp(data['nTime'])
+    key = "%d-%02d"%(block_date.year, block_date.month)
+    for txn in data['transactions'][1:]:
+      values = []
+      for txout in txn['txOut']:
+        n_transactions[key] += 1
+        v_transactions[key] += txout['value'] 
+        values.append(txout['value'])
+      v_transactions_min[key] += min(values)
+      v_transactions_max[key] += max(values)
+    return True
+
+  scan_blocks(db_dir, db_env, gather_stats)
+
+  db_env.close()
+
+  keys = n_transactions.keys()
+  keys.sort()
+  for k in keys:
+    v = v_transactions[k]/1.0e8
+    v_min = v_transactions_min[k]/1.0e8
+    v_max = v_transactions_max[k]/1.0e8
+    # Columns are:
+    # month n_transactions min max total
+    # ... where min and max add up just the smallest or largest
+    # output in each transaction; the true value of bitcoins
+    # transferred will be somewhere between min and max.
+    # We don't know how many are transfers-to-self, though, and
+    # this will undercount multi-txout-transactions (which is good
+    # right now, because they're mostly used for mining pool
+    # payouts that arguably shouldn't count).
+    print "%s,%d,%.2f,%.2f,%.2f"%(k, n_transactions[k], v_min, v_max, v)
+
+if __name__ == '__main__':
+    main()
diff --git a/bitcointools/testBCDataStream.py b/bitcointools/testBCDataStream.py
new file mode 100644 (file)
index 0000000..5452340
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Unit tests for BCDataStream class
+#
+
+import unittest
+
+import BCDataStream
+
+class Tests(unittest.TestCase):
+  def setUp(self):
+    self.ds = BCDataStream.BCDataStream()
+
+  def testString(self):
+    t = {
+      "\x07setting" : "setting",
+      "\xfd\x00\x07setting" : "setting",
+      "\xfe\x00\x00\x00\x07setting" : "setting",
+      }
+    for (input, output) in t.iteritems():
+      self.ds.clear()
+      self.ds.write(input)
+      got = self.ds.read_string()
+      self.assertEqual(output, got)
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/bitcointools/transaction.py b/bitcointools/transaction.py
new file mode 100644 (file)
index 0000000..962c8e9
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# Code for dumping a single transaction, given its ID
+#
+
+from bsddb.db import *
+import logging
+import os.path
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address
+from util import short_hex
+from deserialize import *
+
+def _read_CDiskTxPos(stream):
+  n_file = stream.read_uint32()
+  n_block_pos = stream.read_uint32()
+  n_tx_pos = stream.read_uint32()
+  return (n_file, n_block_pos, n_tx_pos)
+
+def _dump_tx(datadir, tx_hash, tx_pos):
+  blockfile = open(os.path.join(datadir, "blk%04d.dat"%(tx_pos[0],)), "rb")
+  ds = BCDataStream()
+  ds.map_file(blockfile, tx_pos[2])
+  d = parse_Transaction(ds)
+  print deserialize_Transaction(d)
+  ds.close_file()
+  blockfile.close()
+
+def dump_transaction(datadir, db_env, tx_id):
+  """ Dump a transaction, given hexadecimal tx_id-- either the full ID
+      OR a short_hex version of the id.
+  """
+  db = DB(db_env)
+  try:
+    r = db.open("blkindex.dat", "main", DB_BTREE, DB_THREAD|DB_RDONLY)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open blkindex.dat/main.  Try quitting any running Bitcoin apps.")
+    sys.exit(1)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  n_tx = 0
+  n_blockindex = 0
+
+  key_prefix = "\x02tx"+(tx_id[-4:].decode('hex_codec')[::-1])
+  cursor = db.cursor()
+  (key, value) = cursor.set_range(key_prefix)
+
+  while key.startswith(key_prefix):
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+    hash256 = (kds.read_bytes(32))
+    hash_hex = long_hex(hash256[::-1])
+    version = vds.read_uint32()
+    tx_pos = _read_CDiskTxPos(vds)
+    if (hash_hex.startswith(tx_id) or short_hex(hash256[::-1]).startswith(tx_id)):
+      _dump_tx(datadir, hash256, tx_pos)
+
+    (key, value) = cursor.next()
+
+  db.close()
+
diff --git a/bitcointools/util.py b/bitcointools/util.py
new file mode 100644 (file)
index 0000000..4f0adfd
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Misc util routines
+#
+
+from bsddb.db import *
+
+def long_hex(bytes):
+  return bytes.encode('hex_codec')
+
+def short_hex(bytes):
+  t = bytes.encode('hex_codec')
+  if len(t) < 11:
+    return t
+  return t[0:4]+"..."+t[-4:]
+
+def determine_db_dir():
+  import os
+  import os.path
+  import platform
+  if platform.system() == "Darwin":
+    return os.path.expanduser("~/Library/Application Support/Bitcoin/")
+  elif platform.system() == "Windows":
+    return os.path.join(os.environ['APPDATA'], "Bitcoin")
+  return os.path.expanduser("~/.bitcoin")
+
+def create_env(db_dir=None):
+  if db_dir is None:
+    db_dir = determine_db_dir()
+  db_env = DBEnv(0)
+  r = db_env.open(db_dir,
+                  (DB_CREATE|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|
+                   DB_INIT_TXN|DB_THREAD|DB_RECOVER))
+  return db_env
diff --git a/bitcointools/wallet.py b/bitcointools/wallet.py
new file mode 100644 (file)
index 0000000..a41d3a6
--- /dev/null
@@ -0,0 +1,349 @@
+#
+# Code for parsing the wallet.dat file
+#
+
+from bsddb.db import *
+import logging
+import re
+import sys
+import time
+
+from BCDataStream import *
+from base58 import public_key_to_bc_address, bc_address_to_hash_160, hash_160
+from util import short_hex, long_hex
+from deserialize import *
+
+def open_wallet(db_env, writable=False):
+  db = DB(db_env)
+  flags = DB_THREAD | (DB_CREATE if writable else DB_RDONLY)
+  try:
+    r = db.open("wallet.dat", "main", DB_BTREE, flags)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open wallet.dat/main. Try quitting Bitcoin and running this again.")
+    sys.exit(1)
+  
+  return db
+
+def parse_wallet(db, item_callback):
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  for (key, value) in db.items():
+    d = { }
+
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+
+    d["__key__"] = key
+    d["__value__"] = value
+    d["__type__"] = type
+
+    try:
+      if type == "tx":
+        d["tx_id"] = kds.read_bytes(32)
+        d.update(parse_WalletTx(vds))
+      elif type == "name":
+        d['hash'] = kds.read_string()
+        d['name'] = vds.read_string()
+      elif type == "version":
+        d['version'] = vds.read_uint32()
+      elif type == "setting":
+        d['setting'] = kds.read_string()
+        d['value'] = parse_setting(d['setting'], vds)
+      elif type == "key":
+        d['public_key'] = kds.read_bytes(kds.read_compact_size())
+        d['private_key'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "wkey":
+        d['public_key'] = kds.read_bytes(kds.read_compact_size())
+        d['private_key'] = vds.read_bytes(vds.read_compact_size())
+        d['created'] = vds.read_int64()
+        d['expires'] = vds.read_int64()
+        d['comment'] = vds.read_string()
+      elif type == "ckey":
+        d['public_key'] = kds.read_bytes(kds.read_compact_size())
+        d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "mkey":
+        d['nID'] = kds.read_int32()
+        d['crypted_key'] = vds.read_bytes(vds.read_compact_size())
+        d['salt'] = vds.read_bytes(vds.read_compact_size())
+        d['nDerivationMethod'] = vds.read_int32()
+        d['nDeriveIterations'] = vds.read_int32()
+        d['vchOtherDerivationParameters'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "defaultkey":
+        d['key'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "pool":
+        d['n'] = kds.read_int64()
+        d['nVersion'] = vds.read_int32()
+        d['nTime'] = vds.read_int64()
+        d['public_key'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "acc":
+        d['account'] = kds.read_string()
+        d['nVersion'] = vds.read_int32()
+        d['public_key'] = vds.read_bytes(vds.read_compact_size())
+      elif type == "acentry":
+        d['account'] = kds.read_string()
+        d['n'] = kds.read_uint64()
+        d['nVersion'] = vds.read_int32()
+        d['nCreditDebit'] = vds.read_int64()
+        d['nTime'] = vds.read_int64()
+        d['otherAccount'] = vds.read_string()
+        d['comment'] = vds.read_string()
+      elif type == "bestblock":
+        d['nVersion'] = vds.read_int32()
+        d.update(parse_BlockLocator(vds))
+      else:
+        print "Unknown key type: "+type
+      
+      item_callback(type, d)
+
+    except Exception, e:
+      print("ERROR parsing wallet.dat, type %s"%type)
+      print("key data in hex: %s"%key.encode('hex_codec'))
+      print("value data in hex: %s"%value.encode('hex_codec'))
+  
+def update_wallet(db, type, data):
+  """Write a single item to the wallet.
+  db must be open with writable=True.
+  type and data are the type code and data dictionary as parse_wallet would
+  give to item_callback.
+  data's __key__, __value__ and __type__ are ignored; only the primary data
+  fields are used.
+  """
+  d = data
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  # Write the type code to the key
+  kds.write_string(type)
+  vds.write("")             # Ensure there is something
+
+  try:
+    if type == "tx":
+      raise NotImplementedError("Writing items of type 'tx'")
+      kds.write(d['tx_id'])
+      #d.update(parse_WalletTx(vds))
+    elif type == "name":
+      kds.write(d['hash'])
+      vds.write(d['name'])
+    elif type == "version":
+      vds.write_uint32(d['version'])
+    elif type == "setting":
+      raise NotImplementedError("Writing items of type 'setting'")
+      kds.write_string(d['setting'])
+      #d['value'] = parse_setting(d['setting'], vds)
+    elif type == "key":
+      kds.write_string(d['public_key'])
+      vds.write_string(d['private_key'])
+    elif type == "wkey":
+      kds.write_string(d['public_key'])
+      vds.write_string(d['private_key'])
+      vds.write_int64(d['created'])
+      vds.write_int64(d['expires'])
+      vds.write_string(d['comment'])
+    elif type == "ckey":
+      kds.write_string(d['public_key'])
+      kds.write_string(d['crypted_key'])
+    elif type == "mkey":
+      kds.write_int32(d['nID'])
+      vds.write_string(d['crypted_key'])
+      vds.write_string(d['salt'])
+      vds.write_int32(d['nDeriveIterations'])
+      vds.write_int32(d['nDerivationMethod'])
+      vds.write_string(d['vchOtherDerivationParameters'])
+    elif type == "defaultkey":
+      vds.write_string(d['key'])
+    elif type == "pool":
+      kds.write_int64(d['n'])
+      vds.write_int32(d['nVersion'])
+      vds.write_int64(d['nTime'])
+      vds.write_string(d['public_key'])
+    elif type == "acc":
+      kds.write_string(d['account'])
+      vds.write_int32(d['nVersion'])
+      vds.write_string(d['public_key'])
+    elif type == "acentry":
+      kds.write_string(d['account'])
+      kds.write_uint64(d['n'])
+      vds.write_int32(d['nVersion'])
+      vds.write_int64(d['nCreditDebit'])
+      vds.write_int64(d['nTime'])
+      vds.write_string(d['otherAccount'])
+      vds.write_string(d['comment'])
+    elif type == "bestblock":
+      vds.write_int32(d['nVersion'])
+      vds.write_compact_size(len(d['hashes']))
+      for h in d['hashes']:
+        vds.write(h)
+    else:
+      print "Unknown key type: "+type
+
+    # Write the key/value pair to the database
+    db.put(kds.input, vds.input)
+
+  except Exception, e:
+    print("ERROR writing to wallet.dat, type %s"%type)
+    print("data dictionary: %r"%data)
+
+def dump_wallet(db_env, print_wallet, print_wallet_transactions, transaction_filter):
+  db = open_wallet(db_env)
+
+  wallet_transactions = []
+  transaction_index = { }
+  owner_keys = { }
+
+  def item_callback(type, d):
+    if type == "tx":
+      wallet_transactions.append( d )
+      transaction_index[d['tx_id']] = d
+    elif type == "key":
+      owner_keys[public_key_to_bc_address(d['public_key'])] = d['private_key']
+    elif type == "ckey":
+      owner_keys[public_key_to_bc_address(d['public_key'])] = d['crypted_key']
+
+    if not print_wallet:
+      return
+    if type == "tx":
+      return
+    elif type == "name":
+      print("ADDRESS "+d['hash']+" : "+d['name'])
+    elif type == "version":
+      print("Version: %d"%(d['version'],))
+    elif type == "setting":
+      print(d['setting']+": "+str(d['value']))
+    elif type == "key":
+      print("PubKey "+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
+            ": PriKey "+ short_hex(d['private_key']))
+    elif type == "wkey":
+      print("WPubKey 0x"+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
+            ": WPriKey 0x"+ short_hex(d['crypted_key']))
+      print(" Created: "+time.ctime(d['created'])+" Expires: "+time.ctime(d['expires'])+" Comment: "+d['comment'])
+    elif type == "ckey":
+      print("PubKey "+ short_hex(d['public_key']) + " " + public_key_to_bc_address(d['public_key']) +
+            ": Encrypted PriKey "+ short_hex(d['crypted_key']))
+    elif type == "mkey":
+      print("Master Key %d"%(d['nID']) + ": 0x"+ short_hex(d['crypted_key']) +
+            ", Salt: 0x"+ short_hex(d['salt']) +
+            ". Passphrase hashed %d times with method %d with other parameters 0x"%(d['nDeriveIterations'], d['nDerivationMethod']) +
+            long_hex(d['vchOtherDerivationParameters']))
+    elif type == "defaultkey":
+      print("Default Key: 0x"+ short_hex(d['key']) + " " + public_key_to_bc_address(d['key']))
+    elif type == "pool":
+      print("Change Pool key %d: %s (Time: %s)"% (d['n'], public_key_to_bc_address(d['public_key']), time.ctime(d['nTime'])))
+    elif type == "acc":
+      print("Account %s (current key: %s)"%(d['account'], public_key_to_bc_address(d['public_key'])))
+    elif type == "acentry":
+      print("Move '%s' %d (other: '%s', time: %s, entry %d) %s"%
+            (d['account'], d['nCreditDebit'], d['otherAccount'], time.ctime(d['nTime']), d['n'], d['comment']))
+    elif type == "bestblock":
+      print deserialize_BlockLocator(d)
+    else:
+      print "Unknown key type: "+type
+
+  parse_wallet(db, item_callback)
+
+  if print_wallet_transactions:
+    keyfunc = lambda i: i['timeReceived']
+    for d in sorted(wallet_transactions, key=keyfunc):
+      tx_value = deserialize_WalletTx(d, transaction_index, owner_keys)
+      if len(transaction_filter) > 0 and re.search(transaction_filter, tx_value) is None: continue
+
+      print("==WalletTransaction== "+long_hex(d['tx_id'][::-1]))
+      print(tx_value)
+
+  db.close()
+
+def dump_accounts(db_env):
+  db = open_wallet(db_env)
+
+  kds = BCDataStream()
+  vds = BCDataStream()
+
+  accounts = set()
+
+  for (key, value) in db.items():
+    kds.clear(); kds.write(key)
+    vds.clear(); vds.write(value)
+
+    type = kds.read_string()
+
+    if type == "acc":
+      accounts.add(kds.read_string())
+    elif type == "name":
+      accounts.add(vds.read_string())
+    elif type == "acentry":
+      accounts.add(kds.read_string())
+      # Note: don't need to add otheraccount, because moves are
+      # always double-entry
+
+  for name in sorted(accounts):
+    print(name)
+
+  db.close()
+
+def rewrite_wallet(db_env, destFileName, pre_put_callback=None):
+  db = open_wallet(db_env)
+
+  db_out = DB(db_env)
+  try:
+    r = db_out.open(destFileName, "main", DB_BTREE, DB_CREATE)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open %s."%destFileName)
+    sys.exit(1)
+
+  def item_callback(type, d):
+    if (pre_put_callback is None or pre_put_callback(type, d)):
+      db_out.put(d["__key__"], d["__value__"])
+
+  parse_wallet(db, item_callback)
+
+  db_out.close()
+  db.close()
+
+def trim_wallet(db_env, destFileName, pre_put_callback=None):
+  """Write out ONLY address book public/private keys
+     THIS WILL NOT WRITE OUT 'change' KEYS-- you should
+     send all of your bitcoins to one of your public addresses
+     before calling this.
+  """
+  db = open_wallet(db_env)
+  
+  pubkeys = []
+  def gather_pubkeys(type, d):
+    if type == "name":
+      pubkeys.append(bc_address_to_hash_160(d['hash']))
+  
+  parse_wallet(db, gather_pubkeys)
+
+  db_out = DB(db_env)
+  try:
+    r = db_out.open(destFileName, "main", DB_BTREE, DB_CREATE)
+  except DBError:
+    r = True
+
+  if r is not None:
+    logging.error("Couldn't open %s."%destFileName)
+    sys.exit(1)
+
+  def item_callback(type, d):
+    should_write = False
+    if type in [ 'version', 'name', 'acc' ]:
+      should_write = True
+    if type in [ 'key', 'wkey', 'ckey' ] and hash_160(d['public_key']) in pubkeys:
+      should_write = True
+    if pre_put_callback is not None:
+      should_write = pre_put_callback(type, d, pubkeys)
+    if should_write:
+      db_out.put(d["__key__"], d["__value__"])
+
+  parse_wallet(db, item_callback)
+
+  db_out.close()
+  db.close()
index bc681a0..0accbf9 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin Developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -253,9 +254,9 @@ public:
 };
 
 /** base58-encoded bitcoin addresses.
- * Public-key-hash-addresses have version 0 (or 111 testnet).
+ * Public-key-hash-addresses have version 55 (or 111 testnet).
  * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
- * Script-hash-addresses have version 5 (or 196 testnet).
+ * Script-hash-addresses have version 57 (or 196 testnet).
  * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
  */
 class CBitcoinAddress : public CBase58Data
@@ -263,8 +264,8 @@ class CBitcoinAddress : public CBase58Data
 public:
     enum
     {
-        PUBKEY_ADDRESS = 0,
-        SCRIPT_ADDRESS = 5,
+        PUBKEY_ADDRESS = 55,  // ppcoin: addresses begin with 'P'
+        SCRIPT_ADDRESS = 57,  // ppcoin: addresses begin with 'Q'
         PUBKEY_ADDRESS_TEST = 111,
         SCRIPT_ADDRESS_TEST = 196,
     };
index 307017b..f09c4aa 100644 (file)
@@ -184,6 +184,21 @@ public:
         BN_mpi2bn(pch, p - pch, this);
     }
 
+    uint64 getuint64()
+    {
+        unsigned int nSize = BN_bn2mpi(this, NULL);
+        if (nSize < 4)
+            return 0;
+        std::vector<unsigned char> vch(nSize);
+        BN_bn2mpi(this, &vch[0]);
+        if (vch.size() > 4)
+            vch[4] &= 0x7f;
+        uint64 n = 0;
+        for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
+            ((unsigned char*)&n)[i] = vch[j];
+        return n;
+    }
+
     void setuint256(uint256 n)
     {
         unsigned char pch[sizeof(n) + 6];
index 3553f81..08a1dac 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -9,6 +10,7 @@
 #include "walletdb.h"
 #include "net.h"
 #include "init.h"
+#include "checkpoints.h"
 #include "ui_interface.h"
 #include "bitcoinrpc.h"
 
@@ -61,7 +63,7 @@ double GetDifficulty(const CBlockIndex* blockindex = NULL)
         if (pindexBest == NULL)
             return 1.0;
         else
-            blockindex = pindexBest;
+            blockindex = GetLastBlockIndex(pindexBest, false);
     }
 
     int nShift = (blockindex->nBits >> 24) & 0xff;
@@ -87,7 +89,7 @@ double GetDifficulty(const CBlockIndex* blockindex = NULL)
 int64 AmountFromValue(const Value& value)
 {
     double dAmount = value.get_real();
-    if (dAmount <= 0.0 || dAmount > 21000000.0)
+    if (dAmount <= 0.0 || dAmount > MAX_MONEY)
         throw JSONRPCError(-3, "Invalid amount");
     int64 nAmount = roundint64(dAmount * COIN);
     if (!MoneyRange(nAmount))
@@ -223,10 +225,10 @@ Value stop(const Array& params, bool fHelp)
     if (fHelp || params.size() != 0)
         throw runtime_error(
             "stop\n"
-            "Stop bitcoin server.");
+            "Stop ppcoin server.");
     // Shutdown will take long enough that the response should get back
     StartShutdown();
-    return "bitcoin server stopping";
+    return "ppcoin server stopping";
 }
 
 
@@ -333,13 +335,16 @@ Value getinfo(const Array& params, bool fHelp)
             "Returns an object containing various state info.");
 
     Object obj;
-    obj.push_back(Pair("version",       (int)CLIENT_VERSION));
+    obj.push_back(Pair("version",       FormatFullVersion()));
     obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
     obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
     obj.push_back(Pair("balance",       ValueFromAmount(pwalletMain->GetBalance())));
+    obj.push_back(Pair("newmint",       ValueFromAmount(pwalletMain->GetNewMint())));
+    obj.push_back(Pair("stake",         ValueFromAmount(pwalletMain->GetStake())));
     obj.push_back(Pair("blocks",        (int)nBestHeight));
     obj.push_back(Pair("connections",   (int)vNodes.size()));
     obj.push_back(Pair("proxy",         (fUseProxy ? addrProxy.ToStringIPPort() : string())));
+    obj.push_back(Pair("ip",            addrSeenByPeer.ToStringIP()));
     obj.push_back(Pair("difficulty",    (double)GetDifficulty()));
     obj.push_back(Pair("testnet",       fTestNet));
     obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
@@ -379,7 +384,7 @@ Value getnewaddress(const Array& params, bool fHelp)
     if (fHelp || params.size() > 1)
         throw runtime_error(
             "getnewaddress [account]\n"
-            "Returns a new bitcoin address for receiving payments.  "
+            "Returns a new ppcoin address for receiving payments.  "
             "If [account] is specified (recommended), it is added to the address book "
             "so payments received with the address will be credited to [account].");
 
@@ -446,7 +451,7 @@ Value getaccountaddress(const Array& params, bool fHelp)
     if (fHelp || params.size() != 1)
         throw runtime_error(
             "getaccountaddress <account>\n"
-            "Returns the current bitcoin address for receiving payments to this account.");
+            "Returns the current ppcoin address for receiving payments to this account.");
 
     // Parse the account first so we don't generate a key if there's an error
     string strAccount = AccountFromValue(params[0]);
@@ -464,12 +469,12 @@ Value setaccount(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() < 1 || params.size() > 2)
         throw runtime_error(
-            "setaccount <bitcoinaddress> <account>\n"
+            "setaccount <ppcoinaddress> <account>\n"
             "Sets the account associated with the given address.");
 
     CBitcoinAddress address(params[0].get_str());
     if (!address.IsValid())
-        throw JSONRPCError(-5, "Invalid bitcoin address");
+        throw JSONRPCError(-5, "Invalid ppcoin address");
 
 
     string strAccount;
@@ -494,12 +499,12 @@ Value getaccount(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() != 1)
         throw runtime_error(
-            "getaccount <bitcoinaddress>\n"
+            "getaccount <ppcoinaddress>\n"
             "Returns the account associated with the given address.");
 
     CBitcoinAddress address(params[0].get_str());
     if (!address.IsValid())
-        throw JSONRPCError(-5, "Invalid bitcoin address");
+        throw JSONRPCError(-5, "Invalid ppcoin address");
 
     string strAccount;
     map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
@@ -532,17 +537,14 @@ Value getaddressesbyaccount(const Array& params, bool fHelp)
 
 Value settxfee(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() < 1 || params.size() > 1)
+    if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
         throw runtime_error(
             "settxfee <amount>\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001");
+            "<amount> is a real and is rounded to 0.01 (cent)\n"
+            "Minimum and default transaction fee per KB is 1 cent");
 
-    // Amount
-    int64 nAmount = 0;
-    if (params[0].get_real() != 0.0)
-        nAmount = AmountFromValue(params[0]);        // rejects 0.0 amounts
-
-    nTransactionFee = nAmount;
+    nTransactionFee = AmountFromValue(params[0]);
+    nTransactionFee = (nTransactionFee / CENT) * CENT;  // round to cent
     return true;
 }
 
@@ -550,17 +552,17 @@ Value sendtoaddress(const Array& params, bool fHelp)
 {
     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
         throw runtime_error(
-            "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001\n"
+            "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.000001\n"
             "requires wallet passphrase to be set with walletpassphrase first");
     if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
         throw runtime_error(
-            "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001");
+            "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.000001");
 
     CBitcoinAddress address(params[0].get_str());
     if (!address.IsValid())
-        throw JSONRPCError(-5, "Invalid bitcoin address");
+        throw JSONRPCError(-5, "Invalid ppcoin address");
 
     // Amount
     int64 nAmount = AmountFromValue(params[1]);
@@ -586,7 +588,7 @@ Value signmessage(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() != 2)
         throw runtime_error(
-            "signmessage <bitcoinaddress> <message>\n"
+            "signmessage <ppcoinaddress> <message>\n"
             "Sign a message with the private key of an address");
 
     if (pwalletMain->IsLocked())
@@ -618,7 +620,7 @@ Value verifymessage(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() != 3)
         throw runtime_error(
-            "verifymessage <bitcoinaddress> <signature> <message>\n"
+            "verifymessage <ppcoinaddress> <signature> <message>\n"
             "Verify a signed message");
 
     string strAddress  = params[0].get_str();
@@ -651,14 +653,14 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() < 1 || params.size() > 2)
         throw runtime_error(
-            "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
-            "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
+            "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
+            "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
 
     // Bitcoin address
     CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
     CScript scriptPubKey;
     if (!address.IsValid())
-        throw JSONRPCError(-5, "Invalid bitcoin address");
+        throw JSONRPCError(-5, "Invalid ppcoin address");
     scriptPubKey.SetBitcoinAddress(address);
     if (!IsMine(*pwalletMain,scriptPubKey))
         return (double)0.0;
@@ -673,7 +675,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
     {
         const CWalletTx& wtx = (*it).second;
-        if (wtx.IsCoinBase() || !wtx.IsFinal())
+        if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
             continue;
 
         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -720,7 +722,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
     for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
     {
         const CWalletTx& wtx = (*it).second;
-        if (wtx.IsCoinBase() || !wtx.IsFinal())
+        if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
             continue;
 
         BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -873,18 +875,18 @@ Value sendfrom(const Array& params, bool fHelp)
 {
     if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
         throw runtime_error(
-            "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001\n"
+            "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.000001\n"
             "requires wallet passphrase to be set with walletpassphrase first");
     if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
         throw runtime_error(
-            "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
-            "<amount> is a real and is rounded to the nearest 0.00000001");
+            "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+            "<amount> is a real and is rounded to the nearest 0.000001");
 
     string strAccount = AccountFromValue(params[0]);
     CBitcoinAddress address(params[1].get_str());
     if (!address.IsValid())
-        throw JSONRPCError(-5, "Invalid bitcoin address");
+        throw JSONRPCError(-5, "Invalid ppcoin address");
     int64 nAmount = AmountFromValue(params[2]);
     int nMinDepth = 1;
     if (params.size() > 3)
@@ -945,7 +947,7 @@ Value sendmany(const Array& params, bool fHelp)
     {
         CBitcoinAddress address(s.name_);
         if (!address.IsValid())
-            throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_);
+            throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
 
         if (setAddress.count(address))
             throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
@@ -961,6 +963,8 @@ Value sendmany(const Array& params, bool fHelp)
 
     if (pwalletMain->IsLocked())
         throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
+    if (fWalletUnlockStakeOnly)
+        throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
 
     // Check funds
     int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
@@ -1086,7 +1090,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
     {
         const CWalletTx& wtx = (*it).second;
 
-        if (wtx.IsCoinBase() || !wtx.IsFinal())
+        if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
             continue;
 
         int nDepth = wtx.GetDepthInMainChain();
@@ -1586,17 +1590,18 @@ void ThreadCleanWalletPassphrase(void* parg)
 
 Value walletpassphrase(const Array& params, bool fHelp)
 {
-    if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+    if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
         throw runtime_error(
-            "walletpassphrase <passphrase> <timeout>\n"
-            "Stores the wallet decryption key in memory for <timeout> seconds.");
+            "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
+            "Stores the wallet decryption key in memory for <timeout> seconds.\n"
+            "stakeonly is optional true/false allowing only stake creation.");
     if (fHelp)
         return true;
     if (!pwalletMain->IsCrypted())
         throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
 
     if (!pwalletMain->IsLocked())
-        throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
+        throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
 
     // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
     SecureString strWalletPass;
@@ -1619,6 +1624,12 @@ Value walletpassphrase(const Array& params, bool fHelp)
     int64* pnSleepTime = new int64(params[1].get_int64());
     CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
 
+    // ppcoin: if user OS account compromised prevent trivial sendmoney commands
+    if (params.size() > 2)
+        fWalletUnlockStakeOnly = params[2].get_bool();
+    else
+        fWalletUnlockStakeOnly = false;
+
     return Value::null;
 }
 
@@ -1708,7 +1719,7 @@ Value encryptwallet(const Array& params, bool fHelp)
     // slack space in .dat files; that is bad if the old data is
     // unencrypted private keys.  So:
     StartShutdown();
-    return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet";
+    return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
 }
 
 
@@ -1716,8 +1727,8 @@ Value validateaddress(const Array& params, bool fHelp)
 {
     if (fHelp || params.size() != 1)
         throw runtime_error(
-            "validateaddress <bitcoinaddress>\n"
-            "Return information about <bitcoinaddress>.");
+            "validateaddress <ppcoinaddress>\n"
+            "Return information about <ppcoinaddress>.");
 
     CBitcoinAddress address(params[0].get_str());
     bool isValid = address.IsValid();
@@ -1779,10 +1790,10 @@ Value getwork(const Array& params, bool fHelp)
             "If [data] is specified, tries to solve the block and returns true if it was successful.");
 
     if (vNodes.empty())
-        throw JSONRPCError(-9, "Bitcoin is not connected!");
+        throw JSONRPCError(-9, "PPCoin is not connected!");
 
     if (IsInitialBlockDownload())
-        throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+        throw JSONRPCError(-10, "PPCoin is downloading blocks...");
 
     typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
     static mapNewBlock_t mapNewBlock;
@@ -1812,7 +1823,7 @@ Value getwork(const Array& params, bool fHelp)
             nStart = GetTime();
 
             // Create new block
-            pblock = CreateNewBlock(reservekey);
+            pblock = CreateNewBlock(pwalletMain, true);
             if (!pblock)
                 throw JSONRPCError(-7, "Out of memory");
             vNewBlock.push_back(pblock);
@@ -1865,6 +1876,8 @@ Value getwork(const Array& params, bool fHelp)
         pblock->nNonce = pdata->nNonce;
         pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
         pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+        if (!pblock->SignBlock(*pwalletMain))
+            throw JSONRPCError(-100, "Unable to sign block");
 
         return CheckWork(pblock, *pwalletMain, reservekey);
     }
@@ -1891,10 +1904,10 @@ Value getmemorypool(const Array& params, bool fHelp)
     if (params.size() == 0)
     {
         if (vNodes.empty())
-            throw JSONRPCError(-9, "Bitcoin is not connected!");
+            throw JSONRPCError(-9, "PPCoin is not connected!");
 
         if (IsInitialBlockDownload())
-            throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+            throw JSONRPCError(-10, "PPCoin is downloading blocks...");
 
         static CReserveKey reservekey(pwalletMain);
 
@@ -1913,7 +1926,7 @@ Value getmemorypool(const Array& params, bool fHelp)
             // Create new block
             if(pblock)
                 delete pblock;
-            pblock = CreateNewBlock(reservekey);
+            pblock = CreateNewBlock(pwalletMain);
             if (!pblock)
                 throw JSONRPCError(-7, "Out of memory");
         }
@@ -1924,7 +1937,7 @@ Value getmemorypool(const Array& params, bool fHelp)
 
         Array transactions;
         BOOST_FOREACH(CTransaction tx, pblock->vtx) {
-            if(tx.IsCoinBase())
+            if(tx.IsCoinBase() || tx.IsCoinStake())
                 continue;
 
             CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -1996,13 +2009,265 @@ Value getblock(const Array& params, bool fHelp)
 }
 
 
+// ppcoin: get information of sync-checkpoint
+Value getcheckpoint(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() != 0)
+        throw runtime_error(
+            "getcheckpoint\n"
+            "Show info of synchronized checkpoint.\n");
+
+    Object result;
+    CBlockIndex* pindexCheckpoint;
+    
+    result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
+    pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint];        
+    result.push_back(Pair("height", pindexCheckpoint->nHeight));
+    result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str()));
+    
+    return result;
+}
+
+
+// ppcoin: reserve balance from being staked for network protection
+Value reservebalance(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 2)
+        throw runtime_error(
+            "reservebalance [<reserve> [amount]]\n"
+            "<reserve> is true or false to turn balance reserve on or off.\n"
+            "<amount> is a real and rounded to cent.\n"
+            "Set reserve amount not participating in network protection.\n"
+            "If no parameters provided current setting is printed.\n");
+
+    if (params.size() > 0)
+    {
+        bool fReserve = params[0].get_bool();
+        if (fReserve)
+        {
+            if (params.size() == 1)
+                throw runtime_error("must provide amount to reserve balance.\n");
+            int64 nAmount = AmountFromValue(params[1]);
+            nAmount = (nAmount / CENT) * CENT;  // round to cent
+            if (nAmount < 0)
+                throw runtime_error("amount cannot be negative.\n");
+            // TODO: handle persistence of nBalanceReserve
+            // settings removed since bitcoin 0.6
+            // WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
+            nBalanceReserve = nAmount;
+        }
+        else
+        {
+            if (params.size() > 1)
+                throw runtime_error("cannot specify amount to turn off reserve.\n");
+            // TODO: handle persistence of nBalanceReserve
+            // settings removed since bitcoin 0.6
+            // WriteSetting("nBalanceReserve", nBalanceReserve = 0);
+            nBalanceReserve = 0;
+        }
+    }
+
+    Object result;
+    result.push_back(Pair("reserve", (nBalanceReserve > 0)));
+    result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
+    return result;
+}
+
+
+// ppcoin: check wallet integrity
+Value checkwallet(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 0)
+        throw runtime_error(
+            "checkwallet\n"
+            "Check wallet for integrity.\n");
+
+    int nMismatchSpent;
+    int64 nBalanceInQuestion;
+    if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
+    {
+        Object result;
+        result.push_back(Pair("mismatched spent coins", nMismatchSpent));
+        result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
+        return result;
+    }
+    return Value::null;
+}
+
+
+// ppcoin: repair wallet
+Value repairwallet(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 0)
+        throw runtime_error(
+            "repairwallet\n"
+            "Repair wallet if checkwallet reports any problem.\n");
+
+    int nMismatchSpent;
+    int64 nBalanceInQuestion;
+    pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
+    Object result;
+    if (nMismatchSpent == 0)
+    {
+        result.push_back(Pair("wallet check passed", true));
+    }
+    else
+    {
+        result.push_back(Pair("mismatched spent coins", nMismatchSpent));
+        result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
+    }
+    return result;
+}
+
+// ppcoin: make a public-private key pair
+Value makekeypair(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 1)
+        throw runtime_error(
+            "makekeypair [prefix]\n"
+            "Make a public/private key pair.\n"
+            "[prefix] is optional preferred prefix for the public key.\n");
+
+    string strPrefix = "";
+    if (params.size() > 0)
+        strPrefix = params[0].get_str();
+    CKey key;
+    int nCount = 0;
+    do
+    {
+        key.MakeNewKey(false);
+        nCount++;
+    } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
+
+    if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()))
+        return Value::null;
+
+    CPrivKey vchPrivKey = key.GetPrivKey();
+    Object result;
+    result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
+    result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
+    return result;
+}
+
+extern CCriticalSection cs_mapAlerts;
+extern map<uint256, CAlert> mapAlerts;
+
+// ppcoin: send alert.  
+// There is a known deadlock situation with ThreadMessageHandler
+// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
+// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
+Value sendalert(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() < 5)
+       throw runtime_error(
+            "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
+            "<message> is the alert text message\n"
+            "<privatekey> is hex string of alert master private key\n"
+            "<minver> is the minimum applicable client version\n"
+            "<maxver> is the maximum applicable client version\n"
+            "<id> is the alert id\n"
+            "[cancelupto] cancels all alert id's up to this number\n"
+            "Returns true or false.");    
+
+    CAlert alert;
+    CKey key;
+
+    alert.strStatusBar = params[0].get_str();
+    alert.nMinVer = params[2].get_int();
+    alert.nMaxVer = params[3].get_int();
+    alert.nID = params[4].get_int();
+    if (params.size() > 5)
+        alert.nCancel = params[5].get_int();
+    alert.nVersion = PROTOCOL_VERSION;
+    alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
+    alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
+    alert.nPriority = 1;
+
+    CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+    sMsg << (CUnsignedAlert)alert;
+    alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
+    
+    vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
+    key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+    if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
+        throw runtime_error(
+            "Unable to sign alert, check private key?\n");  
+    if(!alert.ProcessAlert()) 
+        throw runtime_error(
+            "Failed to process alert.\n");
+    // Relay alert
+    {
+        LOCK(cs_vNodes);
+        BOOST_FOREACH(CNode* pnode, vNodes)
+            alert.RelayTo(pnode);
+    }
 
+    Object result;
+    result.push_back(Pair("strStatusBar", alert.strStatusBar));
+    result.push_back(Pair("nVersion", alert.nVersion));
+    result.push_back(Pair("nMinVer", alert.nMinVer));
+    result.push_back(Pair("nMaxVer", alert.nMaxVer));
+    result.push_back(Pair("nID", alert.nID));
+    if (alert.nCancel > 0)
+        result.push_back(Pair("nCancel", alert.nCancel));
+    return result;
+}
+
+// ppcoin: send checkpoint
+Value sendcheckpoint(const Array& params, bool fHelp)
+{
+    if (fHelp || params.size() > 2 || params.size() < 1 )
+        throw runtime_error(
+            "sendcheckpoint <privatekey> [checkpointhash]\n"
+            "<privatekey> is hex string of checkpoint master private key\n"
+            "<checkpointhash> is the hash of checkpoint block\n");
 
+    CSyncCheckpoint checkpoint;
+    CKey key;
 
+    // TODO: omit checkpointhash parameter
+    if (params.size() > 1)
+    {
+        checkpoint.hashCheckpoint = uint256(params[1].get_str());
+        if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
+            throw runtime_error(
+                "Provided checkpoint block is not on main chain\n");
+    }
+    else
+    {
+        checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
+        if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
+            throw runtime_error(
+                "Unable to select a more recent sync-checkpoint");
+    }
 
+    CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+    sMsg << (CUnsignedSyncCheckpoint)checkpoint;
+    checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
 
+    vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
+    key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+    if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
+        throw runtime_error(
+            "Unable to sign checkpoint, check private key?\n");
 
+    if(!checkpoint.ProcessSyncCheckpoint(NULL))
+        throw runtime_error(
+            "Failed to process checkpoint.\n");
+    // Relay checkpoint
+    {
+        LOCK(cs_vNodes);
+        BOOST_FOREACH(CNode* pnode, vNodes)
+            checkpoint.RelayTo(pnode);
+    }
 
+    Object result;
+    result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
+    result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
+    result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
+    return result;
+}
 
 
 //
@@ -2059,6 +2324,13 @@ static const CRPCCommand vRPCCommands[] =
     { "listsinceblock",         &listsinceblock,         false },
     { "dumpprivkey",            &dumpprivkey,            false },
     { "importprivkey",          &importprivkey,          false },
+    { "getcheckpoint",          &getcheckpoint,          true },
+    { "reservebalance",         &reservebalance,         false},
+    { "checkwallet",            &checkwallet,            false},
+    { "repairwallet",           &repairwallet,           false},
+    { "makekeypair",            &makekeypair,            false},
+    { "sendalert",              &sendalert,              false},
+    { "sendcheckpoint",         &sendcheckpoint,         false},
 };
 
 CRPCTable::CRPCTable()
@@ -2092,7 +2364,7 @@ string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeader
 {
     ostringstream s;
     s << "POST / HTTP/1.1\r\n"
-      << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
+      << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
       << "Host: 127.0.0.1\r\n"
       << "Content-Type: application/json\r\n"
       << "Content-Length: " << strMsg.size() << "\r\n"
@@ -2123,7 +2395,7 @@ static string HTTPReply(int nStatus, const string& strMsg)
     if (nStatus == 401)
         return strprintf("HTTP/1.0 401 Authorization Required\r\n"
             "Date: %s\r\n"
-            "Server: bitcoin-json-rpc/%s\r\n"
+            "Server: ppcoin-json-rpc/%s\r\n"
             "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
             "Content-Type: text/html\r\n"
             "Content-Length: 296\r\n"
@@ -2150,7 +2422,7 @@ static string HTTPReply(int nStatus, const string& strMsg)
             "Connection: close\r\n"
             "Content-Length: %d\r\n"
             "Content-Type: application/json\r\n"
-            "Server: bitcoin-json-rpc/%s\r\n"
+            "Server: ppcoin-json-rpc/%s\r\n"
             "\r\n"
             "%s",
         nStatus,
@@ -2364,7 +2636,7 @@ void ThreadRPCServer2(void* parg)
     {
         unsigned char rand_pwd[32];
         RAND_bytes(rand_pwd, 32);
-        string strWhatAmI = "To use bitcoind";
+        string strWhatAmI = "To use ppcoind";
         if (mapArgs.count("-server"))
             strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
         else if (mapArgs.count("-daemon"))
@@ -2388,7 +2660,7 @@ void ThreadRPCServer2(void* parg)
     asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
 
     asio::io_service io_service;
-    ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
+    ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
     ip::tcp::acceptor acceptor(io_service);
     try
     {
@@ -2568,7 +2840,7 @@ Object CallRPC(const string& strMethod, const Array& params)
     SSLStream sslStream(io_service, context);
     SSLIOStreamDevice d(sslStream, fUseSSL);
     iostreams::stream<SSLIOStreamDevice> stream(d);
-    if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
+    if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
         throw runtime_error("couldn't connect to server");
 
     // HTTP basic authentication
@@ -2670,7 +2942,12 @@ int CommandLineRPC(int argc, char *argv[])
         if (strMethod == "listtransactions"       && n > 2) ConvertTo<boost::int64_t>(params[2]);
         if (strMethod == "listaccounts"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
         if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<boost::int64_t>(params[1]);
+        if (strMethod == "walletpassphrase"       && n > 2) ConvertTo<bool>(params[2]);
         if (strMethod == "listsinceblock"         && n > 1) ConvertTo<boost::int64_t>(params[1]);
+        if (strMethod == "sendalert"              && n > 2) ConvertTo<boost::int64_t>(params[2]);
+        if (strMethod == "sendalert"              && n > 3) ConvertTo<boost::int64_t>(params[3]);
+        if (strMethod == "sendalert"              && n > 4) ConvertTo<boost::int64_t>(params[4]);
+        if (strMethod == "sendalert"              && n > 5) ConvertTo<boost::int64_t>(params[5]);
         if (strMethod == "sendmany"               && n > 1)
         {
             string s = params[1].get_str();
@@ -2680,6 +2957,8 @@ int CommandLineRPC(int argc, char *argv[])
             params[1] = v.get_obj();
         }
         if (strMethod == "sendmany"                && n > 2) ConvertTo<boost::int64_t>(params[2]);
+        if (strMethod == "reservebalance"          && n > 0) ConvertTo<bool>(params[0]);
+        if (strMethod == "reservebalance"          && n > 1) ConvertTo<double>(params[1]);
         if (strMethod == "addmultisigaddress"      && n > 0) ConvertTo<boost::int64_t>(params[0]);
         if (strMethod == "addmultisigaddress"      && n > 1)
         {
index 67e2c4c..abd72d4 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -7,12 +8,13 @@
 
 #include "checkpoints.h"
 
+#include "db.h"
 #include "main.h"
 #include "uint256.h"
 
 namespace Checkpoints
 {
-    typedef std::map<int, uint256> MapCheckpoints;
+    typedef std::map<int, uint256> MapCheckpoints;   // hardened checkpoints
 
     //
     // What makes a good checkpoint block?
@@ -23,16 +25,10 @@ namespace Checkpoints
     //
     static MapCheckpoints mapCheckpoints =
         boost::assign::map_list_of
-        ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
-        ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
-        ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
-        (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
-        (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
-        (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"))
-        (185333, uint256("0x00000000000002334c71b8706940c20348af897a9cfc0f1a6dab0d14d4ceb815"))
-        ;
-
-    bool CheckBlock(int nHeight, const uint256& hash)
+        ( 0, hashGenesisBlockOfficial )
+        ; // ppcoin: no checkpoint yet; to be created in future releases
+
+    bool CheckHardened(int nHeight, const uint256& hash)
     {
         if (fTestNet) return true; // Testnet has no checkpoints
 
@@ -50,7 +46,12 @@ namespace Checkpoints
 
     CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
     {
-        if (fTestNet) return NULL;
+        if (fTestNet) {
+            std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hashGenesisBlock);
+            if (t != mapBlockIndex.end())
+                return t->second;
+            return NULL;
+        }
 
         BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
         {
@@ -61,4 +62,293 @@ namespace Checkpoints
         }
         return NULL;
     }
+
+    // ppcoin: synchronized checkpoint (centrally broadcasted)
+    uint256 hashSyncCheckpoint = 0;
+    uint256 hashPendingCheckpoint = 0;
+    CSyncCheckpoint checkpointMessage;
+    CSyncCheckpoint checkpointMessagePending;
+    uint256 hashInvalidCheckpoint = 0;
+    CCriticalSection cs_hashSyncCheckpoint;
+
+    // ppcoin: get last synchronized checkpoint
+    CBlockIndex* GetLastSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (!mapBlockIndex.count(hashSyncCheckpoint))
+            error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+        else
+            return mapBlockIndex[hashSyncCheckpoint];
+        return NULL;
+    }
+
+    // ppcoin: only descendant of current sync-checkpoint is allowed
+    bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
+    {
+        if (!mapBlockIndex.count(hashSyncCheckpoint))
+            return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+        if (!mapBlockIndex.count(hashCheckpoint))
+            return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
+
+        CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
+        CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
+
+        if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
+        {
+            // Received an older checkpoint, trace back from current checkpoint
+            // to the same height of the received checkpoint to verify
+            // that current checkpoint should be a descendant block
+            CBlockIndex* pindex = pindexSyncCheckpoint;
+            while (pindex->nHeight > pindexCheckpointRecv->nHeight)
+                if (!(pindex = pindex->pprev))
+                    return error("ValidateSyncCheckpoint: pprev1 null - block index structure failure");
+            if (pindex->GetBlockHash() != hashCheckpoint)
+            {
+                hashInvalidCheckpoint = hashCheckpoint;
+                return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+            }
+            return false; // ignore older checkpoint
+        }
+
+        // Received checkpoint should be a descendant block of the current
+        // checkpoint. Trace back to the same height of current checkpoint
+        // to verify.
+        CBlockIndex* pindex = pindexCheckpointRecv;
+        while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
+            if (!(pindex = pindex->pprev))
+                return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
+        if (pindex->GetBlockHash() != hashSyncCheckpoint)
+        {
+            hashInvalidCheckpoint = hashCheckpoint;
+            return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+        }
+        return true;
+    }
+
+    bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
+    {
+        CTxDB txdb;
+        txdb.TxnBegin();
+        if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
+        {
+            txdb.TxnAbort();
+            return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        }
+        if (!txdb.TxnCommit())
+            return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        txdb.Close();
+
+        Checkpoints::hashSyncCheckpoint = hashCheckpoint;
+        return true;
+    }
+
+    bool AcceptPendingSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
+        {
+            if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
+            {
+                hashPendingCheckpoint = 0;
+                checkpointMessagePending.SetNull();
+                return false;
+            }
+
+            CTxDB txdb;
+            CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
+            if (!pindexCheckpoint->IsInMainChain())
+            {
+                txdb.TxnBegin();
+                if (!Reorganize(txdb, pindexCheckpoint))
+                {
+                    txdb.TxnAbort();
+                    hashInvalidCheckpoint = hashPendingCheckpoint;
+                    return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+                }
+            }
+            txdb.Close();
+
+            if (!WriteSyncCheckpoint(hashPendingCheckpoint))
+                return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+            hashPendingCheckpoint = 0;
+            checkpointMessage = checkpointMessagePending;
+            checkpointMessagePending.SetNull();
+            printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
+            // relay the checkpoint
+            if (!checkpointMessage.IsNull())
+            {
+                BOOST_FOREACH(CNode* pnode, vNodes)
+                    checkpointMessage.RelayTo(pnode);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    uint256 AutoSelectSyncCheckpoint()
+    {
+        // select a block some time ago
+        CBlockIndex *pindex = mapBlockIndex[hashSyncCheckpoint];
+        while (pindex->pnext && pindex->pnext->GetBlockTime() + CHECKPOINT_MIN_SPAN <= GetAdjustedTime())
+            pindex = pindex->pnext;
+        return pindex->GetBlockHash();
+    }
+
+    // Check against synchronized checkpoint
+    bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
+    {
+        if (fTestNet) return true; // Testnet has no checkpoints
+        int nHeight = pindexPrev->nHeight + 1;
+
+        LOCK(cs_hashSyncCheckpoint);
+        // sync-checkpoint should always be accepted block
+        assert(mapBlockIndex.count(hashSyncCheckpoint));
+        const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
+
+        if (nHeight > pindexSync->nHeight)
+        {
+            // trace back to same height as sync-checkpoint
+            const CBlockIndex* pindex = pindexPrev;
+            while (pindex->nHeight > pindexSync->nHeight)
+                if (!(pindex = pindex->pprev))
+                    return error("CheckSync: pprev null - block index structure failure");
+            if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
+                return false; // only descendant of sync-checkpoint can pass check
+        }
+        if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
+            return false; // same height with sync-checkpoint
+        if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
+            return false; // lower height than sync-checkpoint
+        return true;
+    }
+
+    bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (hashPendingCheckpoint == 0)
+            return false;
+        if (hashBlock == hashPendingCheckpoint)
+            return true;
+        if (mapOrphanBlocks.count(hashPendingCheckpoint) 
+            && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
+            return true;
+        return false;
+    }
+
+    // ppcoin: reset synchronized checkpoint to last hardened checkpoint
+    bool ResetSyncCheckpoint()
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        const uint256& hash = mapCheckpoints.rbegin()->second;
+        if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
+        {
+            // checkpoint block accepted but not yet in main chain
+            printf("ResetSyncCheckpoint: Reorganize to hardened checkpoint %s\n", hash.ToString().c_str());
+            CTxDB txdb;
+            txdb.TxnBegin();
+            if (!Reorganize(txdb, mapBlockIndex[hash]))
+            {
+                txdb.TxnAbort();
+                return error("ResetSyncCheckpoint: Reorganize failed for hardened checkpoint %s", hash.ToString().c_str());
+            }
+            txdb.Close();
+        }
+        else if(!mapBlockIndex.count(hash))
+        {
+            // checkpoint block not yet accepted
+            hashPendingCheckpoint = hash;
+            checkpointMessagePending.SetNull();
+            printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
+        }
+
+        BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
+        {
+            const uint256& hash = i.second;
+            if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
+            {
+                if (!WriteSyncCheckpoint(hash))
+                    return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
+                printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    void AskForPendingSyncCheckpoint(CNode* pfrom)
+    {
+        LOCK(cs_hashSyncCheckpoint);
+        if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
+            pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
+    }
+}
+
+// ppcoin: sync-checkpoint master key
+const std::string CSyncCheckpoint::strMasterPubKey = "0424f20205e5da98ba632bbd278a11a6499585f62bfb2c782377ef59f0251daab8085fc31471bcb8180bc75ed0fa41bb50c7c084511d54015a3a5241d645c7268a";
+
+// ppcoin: verify signature of sync-checkpoint message
+bool CSyncCheckpoint::CheckSignature()
+{
+    CKey key;
+    if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
+        return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
+    if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
+        return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
+
+    // Now unserialize the data
+    CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
+    sMsg >> *(CUnsignedSyncCheckpoint*)this;
+    return true;
+}
+
+// ppcoin: process synchronized checkpoint
+bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
+{
+    if (!CheckSignature())
+        return false;
+
+    LOCK(Checkpoints::cs_hashSyncCheckpoint);
+    if (!mapBlockIndex.count(hashCheckpoint))
+    {
+        // We haven't received the checkpoint chain, keep the checkpoint as pending
+        Checkpoints::hashPendingCheckpoint = hashCheckpoint;
+        Checkpoints::checkpointMessagePending = *this;
+        printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
+        // Ask this guy to fill in what we're missing
+        if (pfrom)
+        {
+            pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
+            // ask directly as well in case rejected earlier by duplicate
+            // proof-of-stake because getblocks may not get it this time
+            pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
+        }
+        return false;
+    }
+
+    if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
+        return false;
+
+    CTxDB txdb;
+    CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
+    if (!pindexCheckpoint->IsInMainChain())
+    {
+        // checkpoint chain received but not yet main chain
+        txdb.TxnBegin();
+        if (!Reorganize(txdb, pindexCheckpoint))
+        {
+            txdb.TxnAbort();
+            Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
+            return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
+        }
+    }
+    txdb.Close();
+
+    if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint))
+        return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str());
+    Checkpoints::checkpointMessage = *this;
+    Checkpoints::hashPendingCheckpoint = 0;
+    Checkpoints::checkpointMessagePending.SetNull();
+    printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str());
+    return true;
 }
index 70e9365..ff815c8 100644 (file)
@@ -1,13 +1,20 @@
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_CHECKPOINT_H
 #define  BITCOIN_CHECKPOINT_H
 
 #include <map>
+#include "net.h"
+#include "util.h"
+
+#define STAKE_MIN_AGE (60 * 60 * 24)      // minimum age for coin age
+#define CHECKPOINT_MIN_SPAN (60 * 60 * 4) // 4 hours checkpoint
 
 class uint256;
 class CBlockIndex;
+class CSyncCheckpoint;
 
 /** Block-chain checkpoints are compiled-in sanity checks.
  * They are updated every release or three.
@@ -15,13 +22,116 @@ class CBlockIndex;
 namespace Checkpoints
 {
     // Returns true if block passes checkpoint checks
-    bool CheckBlock(int nHeight, const uint256& hash);
+    bool CheckHardened(int nHeight, const uint256& hash);
 
     // Return conservative estimate of total number of blocks, 0 if unknown
     int GetTotalBlocksEstimate();
 
     // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
     CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex);
+
+    extern uint256 hashSyncCheckpoint;
+    extern CSyncCheckpoint checkpointMessage;
+    extern uint256 hashInvalidCheckpoint;
+    extern CCriticalSection cs_hashSyncCheckpoint;
+
+    CBlockIndex* GetLastSyncCheckpoint();
+    bool WriteSyncCheckpoint(const uint256& hashCheckpoint);
+    bool AcceptPendingSyncCheckpoint();
+    uint256 AutoSelectSyncCheckpoint();
+    bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev);
+    bool WantedByPendingSyncCheckpoint(uint256 hashBlock);
+    bool ResetSyncCheckpoint();
+    void AskForPendingSyncCheckpoint(CNode* pfrom);
 }
 
+// ppcoin: synchronized checkpoint
+class CUnsignedSyncCheckpoint
+{
+public:
+    int nVersion;
+    uint256 hashCheckpoint;      // checkpoint block
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(this->nVersion);
+        nVersion = this->nVersion;
+        READWRITE(hashCheckpoint);
+    )
+
+    void SetNull()
+    {
+        nVersion = 1;
+        hashCheckpoint = 0;
+    }
+
+    std::string ToString() const
+    {
+        return strprintf(
+                "CSyncCheckpoint(\n"
+                "    nVersion       = %d\n"
+                "    hashCheckpoint = %s\n"
+                ")\n",
+            nVersion,
+            hashCheckpoint.ToString().c_str());
+    }
+
+    void print() const
+    {
+        printf("%s", ToString().c_str());
+    }
+};
+
+class CSyncCheckpoint : public CUnsignedSyncCheckpoint
+{
+public:
+    static const std::string strMasterPubKey;
+
+    std::vector<unsigned char> vchMsg;
+    std::vector<unsigned char> vchSig;
+
+    CSyncCheckpoint()
+    {
+        SetNull();
+    }
+
+    IMPLEMENT_SERIALIZE
+    (
+        READWRITE(vchMsg);
+        READWRITE(vchSig);
+    )
+
+    void SetNull()
+    {
+        CUnsignedSyncCheckpoint::SetNull();
+        vchMsg.clear();
+        vchSig.clear();
+    }
+
+    bool IsNull() const
+    {
+        return (hashCheckpoint == 0);
+    }
+
+    uint256 GetHash() const
+    {
+        return SerializeHash(*this);
+    }
+
+    bool RelayTo(CNode* pnode) const
+    {
+        // returns true if wasn't already sent
+        if (pnode->hashCheckpointKnown != hashCheckpoint)
+        {
+            pnode->hashCheckpointKnown = hashCheckpoint;
+            pnode->PushMessage("checkpoint", *this);
+            return true;
+        }
+        return false;
+    }
+
+    bool CheckSignature();
+    bool ProcessSyncCheckpoint(CNode* pfrom);
+};
+
 #endif
index c67a34c..04f400e 100644 (file)
@@ -1,9 +1,12 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #include "db.h"
+#include "net.h"
+#include "checkpoints.h"
 #include "util.h"
 #include "main.h"
 #include <boost/version.hpp>
@@ -485,14 +488,34 @@ bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
     return Write(string("hashBestChain"), hashBestChain);
 }
 
-bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
+bool CTxDB::ReadBestInvalidTrust(uint64& nBestInvalidTrust)
 {
-    return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
+    return Read(string("nBestInvalidTrust"), nBestInvalidTrust);
 }
 
-bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
+bool CTxDB::WriteBestInvalidTrust(uint64 nBestInvalidTrust)
 {
-    return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
+    return Write(string("nBestInvalidTrust"), nBestInvalidTrust);
+}
+
+bool CTxDB::ReadSyncCheckpoint(uint256& hashCheckpoint)
+{
+    return Read(string("hashSyncCheckpoint"), hashCheckpoint);
+}
+
+bool CTxDB::WriteSyncCheckpoint(uint256 hashCheckpoint)
+{
+    return Write(string("hashSyncCheckpoint"), hashCheckpoint);
+}
+
+bool CTxDB::ReadCheckpointPubKey(string& strPubKey)
+{
+    return Read(string("strCheckpointPubKey"), strPubKey);
+}
+
+bool CTxDB::WriteCheckpointPubKey(const string& strPubKey)
+{
+    return Write(string("strCheckpointPubKey"), strPubKey);
 }
 
 CBlockIndex static * InsertBlockIndex(uint256 hash)
@@ -554,7 +577,10 @@ bool CTxDB::LoadBlockIndex()
             pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);
             pindexNew->nFile          = diskindex.nFile;
             pindexNew->nBlockPos      = diskindex.nBlockPos;
+            pindexNew->nChainTrust    = diskindex.nChainTrust;
             pindexNew->nHeight        = diskindex.nHeight;
+            pindexNew->fProofOfStake  = diskindex.fProofOfStake;
+            pindexNew->prevoutStake   = diskindex.prevoutStake;
             pindexNew->nVersion       = diskindex.nVersion;
             pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
             pindexNew->nTime          = diskindex.nTime;
@@ -567,6 +593,10 @@ bool CTxDB::LoadBlockIndex()
 
             if (!pindexNew->CheckIndex())
                 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
+
+            // ppcoin: build setStakeSeen
+            if (pindexNew->fProofOfStake)
+                setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
         }
         else
         {
@@ -582,21 +612,6 @@ bool CTxDB::LoadBlockIndex()
     if (fRequestShutdown)
         return true;
 
-    // Calculate bnChainWork
-    vector<pair<int, CBlockIndex*> > vSortedByHeight;
-    vSortedByHeight.reserve(mapBlockIndex.size());
-    BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
-    {
-        CBlockIndex* pindex = item.second;
-        vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
-    }
-    sort(vSortedByHeight.begin(), vSortedByHeight.end());
-    BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
-    {
-        CBlockIndex* pindex = item.second;
-        pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
-    }
-
     // Load hashBestChain pointer to end of best chain
     if (!ReadHashBestChain(hashBestChain))
     {
@@ -608,11 +623,16 @@ bool CTxDB::LoadBlockIndex()
         return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
     pindexBest = mapBlockIndex[hashBestChain];
     nBestHeight = pindexBest->nHeight;
-    bnBestChainWork = pindexBest->bnChainWork;
-    printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
+    nBestChainTrust = pindexBest->nChainTrust;
+    printf("LoadBlockIndex(): hashBestChain=%s  height=%d  trust=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, nBestChainTrust);
+
+    // ppcoin: load hashSyncCheckpoint
+    if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
+        return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded");
+    printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
 
-    // Load bnBestInvalidWork, OK if it doesn't exist
-    ReadBestInvalidWork(bnBestInvalidWork);
+    // Load nBestInvalidTrust, OK if it doesn't exist
+    ReadBestInvalidTrust(nBestInvalidTrust);
 
     // Verify blocks in the best chain
     int nCheckLevel = GetArg("-checklevel", 1);
index acb531f..792d5ca 100644 (file)
--- a/src/db.h
+++ b/src/db.h
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_DB_H
@@ -293,8 +294,12 @@ public:
     bool EraseBlockIndex(uint256 hash);
     bool ReadHashBestChain(uint256& hashBestChain);
     bool WriteHashBestChain(uint256 hashBestChain);
-    bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
-    bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
+    bool ReadBestInvalidTrust(uint64& nBestInvalidTrust);
+    bool WriteBestInvalidTrust(uint64 nBestInvalidTrust);
+    bool ReadSyncCheckpoint(uint256& hashCheckpoint);
+    bool WriteSyncCheckpoint(uint256 hashCheckpoint);
+    bool ReadCheckpointPubKey(std::string& strPubKey);
+    bool WriteCheckpointPubKey(const std::string& strPubKey);
     bool LoadBlockIndex();
 };
 
index 281a8ca..65386e8 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #include "db.h"
@@ -73,7 +74,7 @@ void Shutdown(void* parg)
         delete pwalletMain;
         CreateThread(ExitTimeout, NULL);
         Sleep(50);
-        printf("Bitcoin exiting\n\n");
+        printf("PPCoin exiting\n\n");
         fExit = true;
 #ifndef QT_GUI
         // ensure non UI client get's exited here, but let Bitcoin-Qt reach return 0; in bitcoin.cpp
@@ -104,6 +105,7 @@ void HandleSIGTERM(int)
 // Start
 //
 #if !defined(QT_GUI)
+#if !defined(PPCOIN_GENESIS)
 int main(int argc, char* argv[])
 {
     bool fRet = false;
@@ -115,6 +117,7 @@ int main(int argc, char* argv[])
     return 1;
 }
 #endif
+#endif
 
 bool AppInit(int argc, char* argv[])
 {
@@ -175,15 +178,15 @@ bool AppInit2(int argc, char* argv[])
     if (mapArgs.count("-?") || mapArgs.count("--help"))
     {
         string strUsage = string() +
-          _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" +
+          _("PPCoin version") + " " + FormatFullVersion() + "\n\n" +
           _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" +
-            "  bitcoind [options]                   \t  " + "\n" +
-            "  bitcoind [options] <command> [params]\t  " + _("Send command to -server or bitcoind") + "\n" +
-            "  bitcoind [options] help              \t\t  " + _("List commands") + "\n" +
-            "  bitcoind [options] help <command>    \t\t  " + _("Get help for a command") + "\n" +
+            "  ppcoind [options]                   \t  " + "\n" +
+            "  ppcoind [options] <command> [params]\t  " + _("Send command to -server or ppcoind") + "\n" +
+            "  ppcoind [options] help              \t\t  " + _("List commands") + "\n" +
+            "  ppcoind [options] help <command>    \t\t  " + _("Get help for a command") + "\n" +
           _("Options:") + "\n" +
-            "  -conf=<file>     \t\t  " + _("Specify configuration file (default: bitcoin.conf)") + "\n" +
-            "  -pid=<file>      \t\t  " + _("Specify pid file (default: bitcoind.pid)") + "\n" +
+            "  -conf=<file>     \t\t  " + _("Specify configuration file (default: ppcoin.conf)") + "\n" +
+            "  -pid=<file>      \t\t  " + _("Specify pid file (default: ppcoind.pid)") + "\n" +
             "  -gen             \t\t  " + _("Generate coins") + "\n" +
             "  -gen=0           \t\t  " + _("Don't generate coins") + "\n" +
             "  -min             \t\t  " + _("Start minimized") + "\n" +
@@ -194,11 +197,10 @@ bool AppInit2(int argc, char* argv[])
             "  -timeout=<n>     \t  "   + _("Specify connection timeout (in milliseconds)") + "\n" +
             "  -proxy=<ip:port> \t  "   + _("Connect through socks4 proxy") + "\n" +
             "  -dns             \t  "   + _("Allow DNS lookups for addnode and connect") + "\n" +
-            "  -port=<port>     \t\t  " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n" +
+            "  -port=<port>     \t\t  " + _("Listen for connections on <port> (default: 9901 or testnet: 9903)") + "\n" +
             "  -maxconnections=<n>\t  " + _("Maintain at most <n> connections to peers (default: 125)") + "\n" +
             "  -addnode=<ip>    \t  "   + _("Add a node to connect to and attempt to keep the connection open") + "\n" +
             "  -connect=<ip>    \t\t  " + _("Connect only to the specified node") + "\n" +
-            "  -irc             \t  "   + _("Find peers using internet relay chat (default: 0)") + "\n" +
             "  -listen          \t  "   + _("Accept connections from outside (default: 1)") + "\n" +
 #ifdef QT_GUI
             "  -lang=<lang>     \t\t  " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
@@ -232,7 +234,7 @@ bool AppInit2(int argc, char* argv[])
 #endif
             "  -rpcuser=<user>  \t  "   + _("Username for JSON-RPC connections") + "\n" +
             "  -rpcpassword=<pw>\t  "   + _("Password for JSON-RPC connections") + "\n" +
-            "  -rpcport=<port>  \t\t  " + _("Listen for JSON-RPC connections on <port> (default: 8332)") + "\n" +
+            "  -rpcport=<port>  \t\t  " + _("Listen for JSON-RPC connections on <port> (default: 9902)") + "\n" +
             "  -rpcallowip=<ip> \t\t  " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
             "  -rpcconnect=<ip> \t  "   + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
             "  -blocknotify=<cmd> "     + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
@@ -328,7 +330,7 @@ bool AppInit2(int argc, char* argv[])
     if (!fDebug)
         ShrinkDebugFile();
     printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
-    printf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
+    printf("PPCoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
     printf("Default data directory %s\n", GetDefaultDataDir().string().c_str());
 
     if (GetBoolArg("-loadblockindextest"))
@@ -346,7 +348,7 @@ bool AppInit2(int argc, char* argv[])
     static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
     if (!lock.try_lock())
     {
-        ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s.  Bitcoin is probably already running."), GetDataDir().string().c_str()), _("Bitcoin"), wxOK|wxMODAL);
+        ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s.  PPCoin is probably already running."), GetDataDir().string().c_str()), _("PPCoin"), wxOK|wxMODAL);
         return false;
     }
 
@@ -355,7 +357,7 @@ bool AppInit2(int argc, char* argv[])
     // Load data files
     //
     if (fDaemon)
-        fprintf(stdout, "bitcoin server starting\n");
+        fprintf(stdout, "ppcoin server starting\n");
     int64 nStart;
 
     InitMessage(_("Loading addresses..."));
@@ -392,12 +394,12 @@ bool AppInit2(int argc, char* argv[])
         if (nLoadWalletRet == DB_CORRUPT)
             strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
         else if (nLoadWalletRet == DB_TOO_NEW)
-            strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n";
+            strErrors << _("Error loading wallet.dat: Wallet requires newer version of PPCoin") << "\n";
         else if (nLoadWalletRet == DB_NEED_REWRITE)
         {
-            strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n";
+            strErrors << _("Wallet needed to be rewritten: restart PPCoin to complete") << "\n";
             printf("%s", strErrors.str().c_str());
-            ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL);
+            ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL);
             return false;
         }
         else
@@ -469,7 +471,7 @@ bool AppInit2(int argc, char* argv[])
 
     if (!strErrors.str().empty())
     {
-        ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL);
+        ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL);
         return false;
     }
 
@@ -525,7 +527,7 @@ bool AppInit2(int argc, char* argv[])
         addrProxy = CService(mapArgs["-proxy"], 9050);
         if (!addrProxy.IsValid())
         {
-            ThreadSafeMessageBox(_("Invalid -proxy address"), _("Bitcoin"), wxOK | wxMODAL);
+            ThreadSafeMessageBox(_("Invalid -proxy address"), _("PPCcoin"), wxOK | wxMODAL);
             return false;
         }
     }
@@ -556,7 +558,7 @@ bool AppInit2(int argc, char* argv[])
         std::string strError;
         if (!BindListenPort(strError))
         {
-            ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL);
+            ThreadSafeMessageBox(strError, _("PPCoin"), wxOK | wxMODAL);
             return false;
         }
     }
@@ -574,13 +576,13 @@ bool AppInit2(int argc, char* argv[])
 
     if (mapArgs.count("-paytxfee"))
     {
-        if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
+        if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee) || nTransactionFee < MIN_TX_FEE)
         {
-            ThreadSafeMessageBox(_("Invalid amount for -paytxfee=<amount>"), _("Bitcoin"), wxOK | wxMODAL);
+            ThreadSafeMessageBox(_("Invalid amount for -paytxfee=<amount>"), _("PPCoin"), wxOK | wxMODAL);
             return false;
         }
         if (nTransactionFee > 0.25 * COIN)
-            ThreadSafeMessageBox(_("Warning: -paytxfee is set very high.  This is the transaction fee you will pay if you send a transaction."), _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
+            ThreadSafeMessageBox(_("Warning: -paytxfee is set very high.  This is the transaction fee you will pay if you send a transaction."), _("PPCoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
     }
 
     //
@@ -592,7 +594,7 @@ bool AppInit2(int argc, char* argv[])
     RandAddSeedPerfmon();
 
     if (!CreateThread(StartNode, NULL))
-        ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("Bitcoin"), wxOK | wxMODAL);
+        ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("PPCoin"), wxOK | wxMODAL);
 
     if (fServer)
         CreateThread(ThreadRPCServer, NULL);
index 9a7ff16..25634df 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -28,12 +29,13 @@ CTxMemPool mempool;
 unsigned int nTransactionsUpdated = 0;
 
 map<uint256, CBlockIndex*> mapBlockIndex;
-uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+set<pair<COutPoint, unsigned int> > setStakeSeen;
+uint256 hashGenesisBlock = hashGenesisBlockOfficial;
 static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
 CBlockIndex* pindexGenesisBlock = NULL;
 int nBestHeight = -1;
-CBigNum bnBestChainWork = 0;
-CBigNum bnBestInvalidWork = 0;
+uint64 nBestChainTrust = 0;
+uint64 nBestInvalidTrust = 0;
 uint256 hashBestChain = 0;
 CBlockIndex* pindexBest = NULL;
 int64 nTimeBestReceived = 0;
@@ -42,6 +44,7 @@ CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes
 
 map<uint256, CBlock*> mapOrphanBlocks;
 multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
+set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
 
 map<uint256, CDataStream*> mapOrphanTransactions;
 map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
@@ -56,6 +59,7 @@ int64 nHPSTimerStart;
 
 // Settings
 int64 nTransactionFee = 0;
+int64 nBalanceReserve = 0;
 
 
 
@@ -109,8 +113,20 @@ void static EraseFromWallets(uint256 hash)
 }
 
 // make sure all wallets know about the given transaction, in the given block
-void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false)
+void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true)
 {
+    if (!fConnect)
+    {
+        // ppcoin: wallets need to refund inputs when disconnecting coinstake
+        if (tx.IsCoinStake())
+        {
+            BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+                if (pwallet->IsFromMe(tx))
+                    pwallet->DisableTransaction(tx);
+        }
+        return;
+    }
+
     BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
         pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
 }
@@ -435,8 +451,11 @@ bool CTransaction::CheckTransaction() const
 
     // Check for negative or overflow output values
     int64 nValueOut = 0;
-    BOOST_FOREACH(const CTxOut& txout, vout)
+    for (int i = 0; i < vout.size(); i++)
     {
+        const CTxOut& txout = vout[i];
+        if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake()))
+            return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
         if (txout.nValue < 0)
             return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
         if (txout.nValue > MAX_MONEY)
@@ -482,6 +501,9 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
     // Coinbase is only valid in a block, not as a loose transaction
     if (tx.IsCoinBase())
         return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
+    // ppcoin: coinstake is also only valid in a block, not as a loose transaction
+    if (tx.IsCoinStake())
+        return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx"));
 
     // To help v0.1.5 clients who would see it as a negative number
     if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
@@ -556,7 +578,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
         unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
 
         // Don't accept it if it can't get into a block
-        if (nFees < tx.GetMinFee(1000, true, GMF_RELAY))
+        if (nFees < tx.GetMinFee(1000, false, GMF_RELAY))
             return error("CTxMemPool::accept() : not enough fees");
 
         // Continuously rate-limit free transactions
@@ -586,7 +608,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
 
         // Check against previous transactions
         // This is done last to help prevent CPU exhaustion denial-of-service attacks.
-        if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+        if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
         {
             return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
         }
@@ -684,7 +706,7 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
 
 int CMerkleTx::GetBlocksToMaturity() const
 {
-    if (!IsCoinBase())
+    if (!(IsCoinBase() || IsCoinStake()))
         return 0;
     return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain());
 }
@@ -720,7 +742,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
         // Add previous supporting transactions first
         BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
         {
-            if (!tx.IsCoinBase())
+            if (!(tx.IsCoinBase() || tx.IsCoinStake()))
             {
                 uint256 hash = tx.GetHash();
                 if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
@@ -790,19 +812,61 @@ uint256 static GetOrphanRoot(const CBlock* pblock)
     return pblock->GetHash();
 }
 
-int64 static GetBlockValue(int nHeight, int64 nFees)
+// ppcoin: find block wanted by given orphan block
+uint256 WantedByOrphan(const CBlock* pblockOrphan)
+{
+    // Work back to the first block in the orphan chain
+    while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock))
+        pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock];
+    return pblockOrphan->hashPrevBlock;
+}
+
+int64 static GetProofOfWorkReward(unsigned int nBits)
 {
-    int64 nSubsidy = 50 * COIN;
+    CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
+    CBigNum bnTarget;
+    bnTarget.SetCompact(nBits);
+    CBigNum bnTargetLimit = bnProofOfWorkLimit;
+    bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
+
+    // ppcoin: subsidy is cut in half every 16x multiply of difficulty
+    // A reasonably continuous curve is used to avoid shock to market
+    // (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
+    CBigNum bnLowerBound = CENT;
+    CBigNum bnUpperBound = bnSubsidyLimit;
+    while (bnLowerBound + CENT <= bnUpperBound)
+    {
+        CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
+        if (fDebug && GetBoolArg("-printcreation"))
+            printf("GetProofOfWorkReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
+        if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
+            bnUpperBound = bnMidValue;
+        else
+            bnLowerBound = bnMidValue;
+    }
+
+    int64 nSubsidy = bnUpperBound.getuint64();
+    nSubsidy = (nSubsidy / CENT) * CENT;
+    if (fDebug && GetBoolArg("-printcreation"))
+        printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
 
-    // Subsidy is cut in half every 4 years
-    nSubsidy >>= (nHeight / 210000);
+    return nSubsidy;
+}
 
-    return nSubsidy + nFees;
+// ppcoin: miner's coin stake is rewarded based on coin age spent (coin-days)
+int64 GetProofOfStakeReward(int64 nCoinAge)
+{
+    static int64 nRewardCoinYear = CENT;  // creation amount per coin-year
+    int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+    if (fDebug && GetBoolArg("-printcreation"))
+        printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nCoinAge);
+    return nSubsidy;
 }
 
-static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
-static const int64 nTargetSpacing = 10 * 60;
-static const int64 nInterval = nTargetTimespan / nTargetSpacing;
+static const int64 nTargetTimespan = 7 * 24 * 60 * 60;  // one week
+static const int64 nTargetSpacingStake = 10 * 60;       // ten minutes
+static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
+static const int64 nMaxClockDrift = 2 * 60 * 60;        // two hours
 
 //
 // minimum amount of work that could possibly be required nTime after
@@ -810,85 +874,54 @@ static const int64 nInterval = nTargetTimespan / nTargetSpacing;
 //
 unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
 {
-    // Testnet has min-difficulty blocks
-    // after nTargetSpacing*2 time between blocks:
-    if (fTestNet && nTime > nTargetSpacing*2)
-        return bnProofOfWorkLimit.GetCompact();
-
     CBigNum bnResult;
     bnResult.SetCompact(nBase);
+    bnResult *= 2;
     while (nTime > 0 && bnResult < bnProofOfWorkLimit)
     {
-        // Maximum 400% adjustment...
-        bnResult *= 4;
-        // ... in best-case exactly 4-times-normal target time
-        nTime -= nTargetTimespan*4;
+        // Maximum 200% adjustment per day...
+        bnResult *= 2;
+        nTime -= 24 * 60 * 60;
     }
     if (bnResult > bnProofOfWorkLimit)
         bnResult = bnProofOfWorkLimit;
     return bnResult.GetCompact();
 }
 
-unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock)
+// ppcoin: find last block index up to pindex
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
 {
-    unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact();
-
-    // Genesis block
-    if (pindexLast == NULL)
-        return nProofOfWorkLimit;
-
-    // Only change once per interval
-    if ((pindexLast->nHeight+1) % nInterval != 0)
-    {
-        // Special rules for testnet after 15 Feb 2012:
-        if (fTestNet && pblock->nTime > 1329264000)
-        {
-            // If the new block's timestamp is more than 2* 10 minutes
-            // then allow mining of a min-difficulty block.
-            if (pblock->nTime - pindexLast->nTime > nTargetSpacing*2)
-                return nProofOfWorkLimit;
-            else
-            {
-                // Return the last non-special-min-difficulty-rules-block
-                const CBlockIndex* pindex = pindexLast;
-                while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit)
-                    pindex = pindex->pprev;
-                return pindex->nBits;
-            }
-        }
-
-        return pindexLast->nBits;
-    }
+    while (pindex && (pindex->IsProofOfStake() != fProofOfStake))
+        pindex = pindex->pprev;
+    return pindex;
+}
 
-    // Go back by what we want to be 14 days worth of blocks
-    const CBlockIndex* pindexFirst = pindexLast;
-    for (int i = 0; pindexFirst && i < nInterval-1; i++)
-        pindexFirst = pindexFirst->pprev;
-    assert(pindexFirst);
+unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
+{
+    // Genesis block and first block
+    if (pindexLast == NULL || pindexLast->pprev == NULL)
+        return bnProofOfWorkLimit.GetCompact();
 
-    // Limit adjustment step
-    int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
-    printf("  nActualTimespan = %"PRI64d"  before bounds\n", nActualTimespan);
-    if (nActualTimespan < nTargetTimespan/4)
-        nActualTimespan = nTargetTimespan/4;
-    if (nActualTimespan > nTargetTimespan*4)
-        nActualTimespan = nTargetTimespan*4;
+    const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
+    if (pindexPrev == NULL) 
+        return bnProofOfWorkLimit.GetCompact();
+    const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
+    if (pindexPrevPrev == NULL)
+        return bnProofOfWorkLimit.GetCompact();
+    int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
 
-    // Retarget
+    // ppcoin: target change every block
+    // ppcoin: retarget with exponential moving toward target spacing
     CBigNum bnNew;
-    bnNew.SetCompact(pindexLast->nBits);
-    bnNew *= nActualTimespan;
-    bnNew /= nTargetTimespan;
+    bnNew.SetCompact(pindexPrev->nBits);
+    int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight));
+    int64 nInterval = nTargetTimespan / nTargetSpacing;
+    bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
+    bnNew /= ((nInterval + 1) * nTargetSpacing);
 
     if (bnNew > bnProofOfWorkLimit)
         bnNew = bnProofOfWorkLimit;
 
-    /// debug print
-    printf("GetNextWorkRequired RETARGET\n");
-    printf("nTargetTimespan = %"PRI64d"    nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan);
-    printf("Before: %08x  %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
-    printf("After:  %08x  %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
-
     return bnNew.GetCompact();
 }
 
@@ -931,25 +964,21 @@ bool IsInitialBlockDownload()
 
 void static InvalidChainFound(CBlockIndex* pindexNew)
 {
-    if (pindexNew->bnChainWork > bnBestInvalidWork)
+    if (pindexNew->nChainTrust > nBestInvalidTrust)
     {
-        bnBestInvalidWork = pindexNew->bnChainWork;
-        CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
+        nBestInvalidTrust = pindexNew->nChainTrust;
+        CTxDB().WriteBestInvalidTrust(nBestInvalidTrust);
         MainFrameRepaint();
     }
-    printf("InvalidChainFound: invalid block=%s  height=%d  work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
-    printf("InvalidChainFound:  current best=%s  height=%d  work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
-    if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6)
+    printf("InvalidChainFound: invalid block=%s  height=%d  trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString().c_str());
+    printf("InvalidChainFound:  current best=%s  height=%d  trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
+    if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
         printf("InvalidChainFound: WARNING: Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade.\n");
 }
 
 void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
 {
-    nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
-
-    // Updating time can change work required on testnet:
-    if (fTestNet)
-        nBits = GetNextWorkRequired(pindexPrev, this);
+    nTime = max(GetBlockTime(), GetAdjustedTime());
 }
 
 
@@ -1115,7 +1144,7 @@ unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
     return nSigOps;
 }
 
-bool CTransaction::ConnectInputs(MapPrevTx inputs,
+bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
                                  map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
                                  const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
 {
@@ -1137,11 +1166,15 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs,
             if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
                 return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
 
-            // If prev is coinbase, check that it's matured
-            if (txPrev.IsCoinBase())
+            // If prev is coinbase/coinstake, check that it's matured
+            if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
                 for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
                     if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
-                        return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
+                        return error("ConnectInputs() : tried to spend coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight);
+
+            // ppcoin: check transaction timestamp
+            if (txPrev.nTime > nTime)
+                return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction"));
 
             // Check for negative or overflow input values
             nValueIn += txPrev.vout[prevout.n].nValue;
@@ -1192,16 +1225,32 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs,
             }
         }
 
-        if (nValueIn < GetValueOut())
-            return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
+        if (IsCoinStake())
+        {
+            // ppcoin: coin stake tx earns reward instead of paying fee
+            uint64 nCoinAge;
+            if (!GetCoinAge(txdb, nCoinAge))
+                return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
+            int64 nStakeReward = GetValueOut() - nValueIn;
+            if (nStakeReward > GetProofOfStakeReward(nCoinAge))
+                return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str()));
+        }
+        else
+        {
+            if (nValueIn < GetValueOut())
+                return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
 
-        // Tally transaction fees
-        int64 nTxFee = nValueIn - GetValueOut();
-        if (nTxFee < 0)
-            return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
-        nFees += nTxFee;
-        if (!MoneyRange(nFees))
-            return DoS(100, error("ConnectInputs() : nFees out of range"));
+            // Tally transaction fees
+            int64 nTxFee = nValueIn - GetValueOut();
+            if (nTxFee < 0)
+                return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
+            // ppcoin: enforce transaction fees for every block
+            if (nTxFee < GetMinFee())
+                return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
+            nFees += nTxFee;
+            if (!MoneyRange(nFees))
+                return DoS(100, error("ConnectInputs() : nFees out of range"));
+        }
     }
 
     return true;
@@ -1274,6 +1323,10 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
             return error("DisconnectBlock() : WriteBlockIndex failed");
     }
 
+    // ppcoin: clean up wallet after disconnecting coinstake
+    BOOST_FOREACH(CTransaction& tx, vtx)
+        SyncWithWallets(tx, this, false, false);
+
     return true;
 }
 
@@ -1312,7 +1365,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
     bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
 
     //// issue here: it doesn't know the version
-    unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
+    unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
 
     map<uint256, CTxIndex> mapQueuedChanges;
     int64 nFees = 0;
@@ -1327,7 +1380,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
         nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
 
         MapPrevTx mapInputs;
-        if (!tx.IsCoinBase())
+        if (!(tx.IsCoinBase() || tx.IsCoinStake()))
         {
             bool fInvalid;
             if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
@@ -1345,7 +1398,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
 
             nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
 
-            if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+            if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
                 return false;
         }
 
@@ -1359,8 +1412,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
             return error("ConnectBlock() : UpdateTxIndex failed");
     }
 
-    if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
+    // ppcoin: fees are not collected by miners as in bitcoin
+    // ppcoin: fees are destroyed to compensate the entire network
+    if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
         return false;
+    if (fDebug && GetBoolArg("-printcreation"))
+        printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
 
     // Update block index on disk without changing it in memory.
     // The memory index structure will be changed after the db commits.
@@ -1379,7 +1436,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
     return true;
 }
 
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
 {
     printf("REORGANIZE\n");
 
@@ -1423,7 +1480,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
 
         // Queue memory transactions to resurrect
         BOOST_FOREACH(const CTransaction& tx, block.vtx)
-            if (!tx.IsCoinBase())
+            if (!(tx.IsCoinBase() || tx.IsCoinStake()))
                 vResurrect.push_back(tx);
     }
 
@@ -1539,7 +1596,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
 
         // Reorganize is costly in terms of db load, as it works in a single db transaction.
         // Try to limit how much needs to be done inside
-        while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
+        while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust)
         {
             vpindexSecondary.push_back(pindexIntermediate);
             pindexIntermediate = pindexIntermediate->pprev;
@@ -1587,10 +1644,10 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
     hashBestChain = hash;
     pindexBest = pindexNew;
     nBestHeight = pindexBest->nHeight;
-    bnBestChainWork = pindexNew->bnChainWork;
+    nBestChainTrust = pindexNew->nChainTrust;
     nTimeBestReceived = GetTime();
     nTransactionsUpdated++;
-    printf("SetBestChain: new best=%s  height=%d  work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
+    printf("SetBestChain: new best=%s  height=%d  trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
 
     std::string strCmd = GetArg("-blocknotify", "");
 
@@ -1604,6 +1661,138 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
 }
 
 
+// ppcoin: coinstake must meet hash target according to the protocol:
+// input 0 must meet the formula
+//     hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
+// this ensures that the chance of getting a coinstake is proportional to the
+// amount of coin age one owns.
+// The reason this hash is chosen is the following:
+//   nBits: encodes all past block timestamps, making computing hash in advance
+//          more difficult
+//   txPrev.block.nTime: prevent nodes from guessing a good timestamp to
+//                       generate transaction for future advantage
+//   txPrev.offset: offset of txPrev inside block, to reduce the chance of 
+//                  nodes generating coinstake at the same time
+//   txPrev.nTime: reduce the chance of nodes generating coinstake at the same
+//                 time
+//   txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
+//                  generating coinstake at the same time
+//   block/tx hash should not be used here as they can be generated in vast
+//   quantities so as to generate blocks faster, degrading the system back into
+//   a proof-of-work situation.
+//
+bool CTransaction::CheckProofOfStake(unsigned int nBits) const
+{
+    CBigNum bnTargetPerCoinDay;
+    bnTargetPerCoinDay.SetCompact(nBits);
+    if (!IsCoinStake())
+        return true;
+
+    // Input 0 must match the stake hash target per coin age (nBits)
+    const CTxIn& txin = vin[0];
+
+    // First try finding the previous transaction in database
+    CTxDB txdb("r");
+    CTransaction txPrev;
+    CTxIndex txindex;
+    if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+        return false;  // previous transaction not in main chain
+    txdb.Close();
+    if (nTime < txPrev.nTime)
+        return false;  // Transaction timestamp violation
+
+    // Verify signature
+    if (!VerifySignature(txPrev, *this, 0, true, 0))
+        return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str()));
+
+    // Read block header
+    CBlock block;
+    if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+        return false; // unable to read block of previous transaction
+    if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+        return false; // only count coins meeting min age requirement
+
+    int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+    CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
+    // Calculate hash
+    CDataStream ss(SER_GETHASH, 0);
+    ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
+    if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+        return true;
+    else
+        return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str()));
+}
+
+// ppcoin: total coin age spent in transaction, in the unit of coin-days.
+// Only those coins meeting minimum age requirement counts. As those
+// transactions not in main chain are not currently indexed so we
+// might not find out about their coin age. Older transactions are 
+// guaranteed to be in main chain by sync-checkpoint. This rule is
+// introduced to help nodes establish a consistent view of the coin
+// age (trust score) of competing branches.
+bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const
+{
+    CBigNum bnCentSecond = 0;  // coin age in the unit of cent-seconds
+    nCoinAge = 0;
+
+    if (IsCoinBase())
+        return true;
+
+    BOOST_FOREACH(const CTxIn& txin, vin)
+    {
+        // First try finding the previous transaction in database
+        CTransaction txPrev;
+        CTxIndex txindex;
+        if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+            continue;  // previous transaction not in main chain
+        if (nTime < txPrev.nTime)
+            return false;  // Transaction timestamp violation
+
+        // Read block header
+        CBlock block;
+        if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+            return false; // unable to read block of previous transaction
+        if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+            continue; // only count coins meeting min age requirement
+
+        int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+        bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT;
+
+        if (fDebug && GetBoolArg("-printcoinage"))
+            printf("coin age nValueIn=%-12I64d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str());
+    }
+
+    CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
+    if (fDebug && GetBoolArg("-printcoinage"))
+        printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str());
+    nCoinAge = bnCoinDay.getuint64();
+    return true;
+}
+
+// ppcoin: total coin age spent in block, in the unit of coin-days.
+bool CBlock::GetCoinAge(uint64& nCoinAge) const
+{
+    nCoinAge = 0;
+
+    CTxDB txdb("r");
+    BOOST_FOREACH(const CTransaction& tx, vtx)
+    {
+        uint64 nTxCoinAge;
+        if (tx.GetCoinAge(txdb, nTxCoinAge))
+            nCoinAge += nTxCoinAge;
+        else
+            return false;
+    }
+
+    if (nCoinAge == 0) // block coin age minimum 1 coin-day
+        nCoinAge = 1;
+    if (fDebug && GetBoolArg("-printcoinage"))
+        printf("block coin age total nCoinDays=%"PRI64d"\n", nCoinAge);
+    return true;
+}
+
+
 bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
 {
     // Check for duplicate
@@ -1616,6 +1805,9 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
     if (!pindexNew)
         return error("AddToBlockIndex() : new CBlockIndex failed");
     map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+    if (pindexNew->fProofOfStake) 
+        setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
+
     pindexNew->phashBlock = &((*mi).first);
     map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
     if (miPrev != mapBlockIndex.end())
@@ -1623,7 +1815,12 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
         pindexNew->pprev = (*miPrev).second;
         pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
     }
-    pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork();
+
+    // ppcoin: compute chain trust score
+    uint64 nCoinAge;
+    if (!GetCoinAge(nCoinAge))
+        return error("AddToBlockIndex() : invalid transaction in block");
+    pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge;
 
     CTxDB txdb;
     if (!txdb.TxnBegin())
@@ -1633,7 +1830,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
         return false;
 
     // New best
-    if (pindexNew->bnChainWork > bnBestChainWork)
+    if (pindexNew->nChainTrust > nBestChainTrust)
         if (!SetBestChain(txdb, pindexNew))
             return false;
 
@@ -1664,11 +1861,11 @@ bool CBlock::CheckBlock() const
         return DoS(100, error("CheckBlock() : size limits failed"));
 
     // Check proof of work matches claimed amount
-    if (!CheckProofOfWork(GetHash(), nBits))
+    if (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
         return DoS(50, error("CheckBlock() : proof of work failed"));
 
     // Check timestamp
-    if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
+    if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift)
         return error("CheckBlock() : block timestamp too far in the future");
 
     // First transaction must be coinbase, the rest must not be
@@ -1678,10 +1875,32 @@ bool CBlock::CheckBlock() const
         if (vtx[i].IsCoinBase())
             return DoS(100, error("CheckBlock() : more than one coinbase"));
 
+    // ppcoin: only the second transaction can be the optional coinstake
+    for (int i = 2; i < vtx.size(); i++)
+        if (vtx[i].IsCoinStake())
+            return DoS(100, error("CheckBlock() : coinstake in wrong position"));
+
+    // ppcoin: coinbase output should be empty if proof-of-stake block
+    if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty())
+        return error("CheckBlock() : coinbase output not empty for proof-of-stake block");
+
+    // Check coinbase timestamp
+    if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
+        return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
+
+    // Check coinstake timestamp
+    if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift)
+        return DoS(50, error("CheckBlock() : coinstake timestamp is too early"));
+
     // Check transactions
     BOOST_FOREACH(const CTransaction& tx, vtx)
+    {
         if (!tx.CheckTransaction())
             return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
+        // ppcoin: check transaction timestamp
+        if (GetBlockTime() < (int64)tx.nTime)
+            return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
+    }
 
     // Check for duplicate txids. This is caught by ConnectInputs(),
     // but catching it earlier avoids a potential DoS attack:
@@ -1705,6 +1924,10 @@ bool CBlock::CheckBlock() const
     if (hashMerkleRoot != BuildMerkleTree())
         return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
 
+    // ppcoin: check block signature
+    if (!CheckBlockSignature())
+        return DoS(100, error("CheckBlock() : bad block signature"));
+
     return true;
 }
 
@@ -1722,12 +1945,12 @@ bool CBlock::AcceptBlock()
     CBlockIndex* pindexPrev = (*mi).second;
     int nHeight = pindexPrev->nHeight+1;
 
-    // Check proof of work
-    if (nBits != GetNextWorkRequired(pindexPrev, this))
-        return DoS(100, error("AcceptBlock() : incorrect proof of work"));
+    // Check proof-of-work or proof-of-stake
+    if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
+        return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
 
     // Check timestamp against prev
-    if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
+    if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
         return error("AcceptBlock() : block's timestamp is too early");
 
     // Check that all transactions are finalized
@@ -1735,9 +1958,13 @@ bool CBlock::AcceptBlock()
         if (!tx.IsFinal(nHeight, GetBlockTime()))
             return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
 
-    // Check that the block chain matches the known block chain up to a checkpoint
-    if (!Checkpoints::CheckBlock(nHeight, hash))
-        return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
+    // Check that the block chain matches the known block chain up to a hardened checkpoint
+    if (!Checkpoints::CheckHardened(nHeight, hash))
+        return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight));
+
+    // ppcoin: check that the block satisfies synchronized checkpoint
+    if (!Checkpoints::CheckSync(hash, pindexPrev))
+        return error("AcceptBlock() : rejected by synchronized checkpoint");
 
     // Write block to history file
     if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
@@ -1759,6 +1986,9 @@ bool CBlock::AcceptBlock()
                 pnode->PushInventory(CInv(MSG_BLOCK, hash));
     }
 
+    // ppcoin: check pending sync-checkpoint
+    Checkpoints::AcceptPendingSyncCheckpoint();
+
     return true;
 }
 
@@ -1771,45 +2001,68 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     if (mapOrphanBlocks.count(hash))
         return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
 
+    // ppcoin: check proof-of-stake
+    // Limited duplicity on stake: prevents block flood attack
+    // Duplicate stake allowed only when there is orphan child block
+    if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+        return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
     // Preliminary checks
     if (!pblock->CheckBlock())
         return error("ProcessBlock() : CheckBlock FAILED");
 
-    CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
-    if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
+    // ppcoin: verify hash target and signature of coinstake tx
+    if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
+        return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
+
+    CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
+    if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
     {
         // Extra checks to prevent "fill up memory by spamming with bogus blocks"
         int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
-        if (deltaTime < 0)
-        {
-            if (pfrom)
-                pfrom->Misbehaving(100);
-            return error("ProcessBlock() : block with timestamp before last checkpoint");
-        }
         CBigNum bnNewBlock;
         bnNewBlock.SetCompact(pblock->nBits);
         CBigNum bnRequired;
-        bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
+        bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime));
+
         if (bnNewBlock > bnRequired)
         {
             if (pfrom)
                 pfrom->Misbehaving(100);
-            return error("ProcessBlock() : block with too little proof-of-work");
+            return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
         }
     }
 
+    // ppcoin: ask for pending sync-checkpoint if any
+    if (!IsInitialBlockDownload())
+        Checkpoints::AskForPendingSyncCheckpoint(pfrom);
 
     // If don't already have its previous block, shunt it off to holding area until we get it
     if (!mapBlockIndex.count(pblock->hashPrevBlock))
     {
         printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
         CBlock* pblock2 = new CBlock(*pblock);
+        // ppcoin: check proof-of-stake
+        if (pblock2->IsProofOfStake())
+        {
+            // Limited duplicity on stake: prevents block flood attack
+            // Duplicate stake allowed only when there is orphan child block
+            if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+                return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock2->GetProofOfStake().first.ToString().c_str(), pblock2->GetProofOfStake().second, hash.ToString().c_str());
+            else
+                setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
+        }
         mapOrphanBlocks.insert(make_pair(hash, pblock2));
         mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
 
         // Ask this guy to fill in what we're missing
         if (pfrom)
+        {
             pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
+            // ppcoin: getblocks may not obtain the ancestor block rejected
+            // earlier by duplicate-stake check so we ask for it again directly
+            pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+        }
         return true;
     }
 
@@ -1831,6 +2084,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
             if (pblockOrphan->AcceptBlock())
                 vWorkQueue.push_back(pblockOrphan->GetHash());
             mapOrphanBlocks.erase(pblockOrphan->GetHash());
+            setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake());
             delete pblockOrphan;
         }
         mapOrphanBlocksByPrev.erase(hashPrev);
@@ -1840,7 +2094,53 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     return true;
 }
 
+// ppcoin: sign block
+bool CBlock::SignBlock(const CKeyStore& keystore)
+{
+    vector<valtype> vSolutions;
+    txnouttype whichType;
+    const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
 
+    if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+        return false;
+    if (whichType == TX_PUBKEY)
+    {
+        // Sign
+        const valtype& vchPubKey = vSolutions[0];
+        CKey key;
+        if (!keystore.GetKey(Hash160(vchPubKey), key))
+            return false;
+        if (key.GetPubKey() != vchPubKey)
+            return false;
+        return key.Sign(GetHash(), vchBlockSig);
+    }
+    return false;
+}
+
+// ppcoin: check block signature
+bool CBlock::CheckBlockSignature() const
+{
+    if (GetHash() == hashGenesisBlock)
+        return vchBlockSig.empty();
+
+    vector<valtype> vSolutions;
+    txnouttype whichType;
+    const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
+
+    if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+        return false;
+    if (whichType == TX_PUBKEY)
+    {
+        const valtype& vchPubKey = vSolutions[0];
+        CKey key;
+        if (!key.SetPubKey(vchPubKey))
+            return false;
+        if (vchBlockSig.empty())
+            return false;
+        return key.Verify(GetHash(), vchBlockSig);
+    }
+    return false;
+}
 
 
 
@@ -1910,7 +2210,7 @@ bool LoadBlockIndex(bool fAllowNew)
 {
     if (fTestNet)
     {
-        hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
+        hashGenesisBlock = hashGenesisBlockTestNet;
         bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
         pchMessageStart[0] = 0xfa;
         pchMessageStart[1] = 0xbf;
@@ -1942,21 +2242,21 @@ bool LoadBlockIndex(bool fAllowNew)
         //   vMerkleTree: 4a5e1e
 
         // Genesis block
-        const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
+        const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
         CTransaction txNew;
+        txNew.nTime = 1339538219;
         txNew.vin.resize(1);
         txNew.vout.resize(1);
         txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
-        txNew.vout[0].nValue = 50 * COIN;
-        txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
+        txNew.vout[0].SetEmpty();
         CBlock block;
         block.vtx.push_back(txNew);
         block.hashPrevBlock = 0;
         block.hashMerkleRoot = block.BuildMerkleTree();
         block.nVersion = 1;
-        block.nTime    = 1231006505;
-        block.nBits    = 0x1d00ffff;
-        block.nNonce   = 2083236893;
+        block.nTime    = 1339540307;
+        block.nBits    = bnProofOfWorkLimit.GetCompact();
+        block.nNonce   = 1281822831;
 
         if (fTestNet)
         {
@@ -1969,9 +2269,10 @@ bool LoadBlockIndex(bool fAllowNew)
         printf("%s\n", block.GetHash().ToString().c_str());
         printf("%s\n", hashGenesisBlock.ToString().c_str());
         printf("%s\n", block.hashMerkleRoot.ToString().c_str());
-        assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
+        assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d"));
         block.print();
         assert(block.GetHash() == hashGenesisBlock);
+        assert(block.CheckBlock());
 
         // Start new block file
         unsigned int nFile;
@@ -1980,6 +2281,28 @@ bool LoadBlockIndex(bool fAllowNew)
             return error("LoadBlockIndex() : writing genesis block to disk failed");
         if (!block.AddToBlockIndex(nFile, nBlockPos))
             return error("LoadBlockIndex() : genesis block not accepted");
+
+        // ppcoin: initialize synchronized checkpoint
+        if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock))
+            return error("LoadBlockIndex() : failed to init sync checkpoint");
+    }
+
+    // ppcoin: if checkpoint master key changed must reset sync-checkpoint
+    {
+        CTxDB txdb;
+        string strPubKey = "";
+        if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
+        {
+            // write checkpoint master key to db
+            txdb.TxnBegin();
+            if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+                return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
+            if (!txdb.TxnCommit())
+                return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
+            if (!Checkpoints::ResetSyncCheckpoint())
+                return error("LoadBlockIndex() : failed to reset sync-checkpoint");
+        }
+        txdb.Close();
     }
 
     return true;
@@ -2032,11 +2355,12 @@ void PrintBlockTree()
         // print item
         CBlock block;
         block.ReadFromDisk(pindex);
-        printf("%d (%u,%u) %s  %s  tx %d",
+        printf("%d (%u,%u) %s  %08lx  %s  tx %d",
             pindex->nHeight,
             pindex->nFile,
             pindex->nBlockPos,
             block.GetHash().ToString().substr(0,20).c_str(),
+            block.nBits,
             DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
             block.vtx.size());
 
@@ -2092,12 +2416,18 @@ string GetWarnings(string strFor)
     }
 
     // Longer invalid proof-of-work chain
-    if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6)
+    if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
     {
         nPriority = 2000;
         strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade.";
     }
 
+    if (Checkpoints::hashInvalidCheckpoint != 0)
+    {
+        nPriority = 3000;
+        strStatusBar = strRPC = "WARNING: Invalid checkpoint found!  Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade.";
+    }
+
     // Alerts
     {
         LOCK(cs_mapAlerts);
@@ -2271,6 +2601,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
             return true;
         }
 
+        // ppcoin: record my external IP reported by peer
+        if (addrFrom.IsRoutable() && addrMe.IsRoutable())
+            addrSeenByPeer = addrMe;
+
         // Be shy and don't send version until we hear
         if (pfrom->fInbound)
             pfrom->PushVersion();
@@ -2327,11 +2661,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                 item.second.RelayTo(pfrom);
         }
 
+        // ppcoin: relay sync-checkpoint
+        {
+            LOCK(Checkpoints::cs_hashSyncCheckpoint);
+            if (!Checkpoints::checkpointMessage.IsNull())
+                Checkpoints::checkpointMessage.RelayTo(pfrom);
+        }
+
         pfrom->fSuccessfullyConnected = true;
 
         printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
 
         cPeerBlockCounts.input(pfrom->nStartingHeight);
+
+        // ppcoin: ask for pending sync-checkpoint if any
+        if (!IsInitialBlockDownload())
+            Checkpoints::AskForPendingSyncCheckpoint(pfrom);
     }
 
 
@@ -2774,6 +3119,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         }
     }
 
+    else if (strCommand == "checkpoint")
+    {
+        CSyncCheckpoint checkpoint;
+        vRecv >> checkpoint;
+
+        if (checkpoint.ProcessSyncCheckpoint(pfrom))
+        {
+            // Relay
+            pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
+            LOCK(cs_vNodes);
+            BOOST_FOREACH(CNode* pnode, vNodes)
+                checkpoint.RelayTo(pnode);
+        }
+    }
 
     else
     {
@@ -3180,9 +3539,9 @@ public:
 uint64 nLastBlockTx = 0;
 uint64 nLastBlockSize = 0;
 
-CBlock* CreateNewBlock(CReserveKey& reservekey)
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
 {
-    CBlockIndex* pindexPrev = pindexBest;
+    CReserveKey reservekey(pwallet);
 
     // Create new block
     auto_ptr<CBlock> pblock(new CBlock());
@@ -3199,6 +3558,37 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
     // Add our coinbase tx as first transaction
     pblock->vtx.push_back(txNew);
 
+    // ppcoin: if coinstake available add coinstake tx
+    static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60;  // only initialized at startup
+    CBlockIndex* pindexPrev = pindexBest;
+
+    if (!fProofOfWorkOnly)
+    {
+        while (nLastCoinStakeCheckTime < GetAdjustedTime())
+        {
+            pindexPrev = pindexBest;  // get best block again to avoid getting stale
+            pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+            CTransaction txCoinStake;
+            {
+                static CCriticalSection cs;
+                LOCK(cs);
+                // mining may have been suspended for a while so 
+                // need to take max to satisfy the timestamp protocol
+                nLastCoinStakeCheckTime++;
+                nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
+                txCoinStake.nTime = nLastCoinStakeCheckTime;
+            }
+            if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+            {
+                pblock->vtx.push_back(txCoinStake);
+                pblock->vtx[0].vout[0].SetEmpty();
+                break;
+            }
+        }
+    }
+
+    pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
+
     // Collect memory pool transactions into the block
     int64 nFees = 0;
     {
@@ -3212,7 +3602,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
         for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
         {
             CTransaction& tx = (*mi).second;
-            if (tx.IsCoinBase() || !tx.IsFinal())
+            if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
                 continue;
 
             COrphan* porphan = NULL;
@@ -3271,7 +3661,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
         while (!mapPriority.empty())
         {
             // Take highest priority transaction off priority queue
-            double dPriority = -(*mapPriority.begin()).first;
             CTransaction& tx = *(*mapPriority.begin()).second;
             mapPriority.erase(mapPriority.begin());
 
@@ -3285,9 +3674,12 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
             if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
                 continue;
 
-            // Transaction fee required depends on block size
-            bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
-            int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
+            // Timestamp limit
+            if (tx.nTime > GetAdjustedTime())
+                continue;
+
+            // ppcoin: simplify transaction fee - allow free = false
+            int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
 
             // Connecting shouldn't fail due to dependency on other memory pool transactions
             // because we're already processing them in order of dependency
@@ -3305,7 +3697,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
             if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
                 continue;
 
-            if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
+            if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
                 continue;
             mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
             swap(mapTestPool, mapTestPoolTmp);
@@ -3338,13 +3730,16 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
         printf("CreateNewBlock(): total size %lu\n", nBlockSize);
 
     }
-    pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
+    if (pblock->IsProofOfWork())
+        pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
 
     // Fill in header
     pblock->hashPrevBlock  = pindexPrev->GetBlockHash();
     pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+    pblock->nTime          = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+    pblock->nTime          = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
+    pblock->nTime          = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
     pblock->UpdateTime(pindexPrev);
-    pblock->nBits          = GetNextWorkRequired(pindexPrev, pblock.get());
     pblock->nNonce         = 0;
 
     return pblock.release();
@@ -3419,12 +3814,12 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
     uint256 hash = pblock->GetHash();
     uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
 
-    if (hash > hashTarget)
-        return false;
+    if (hash > hashTarget && pblock->IsProofOfWork())
+        return error("BitcoinMiner : proof-of-work not meeting target");
 
     //// debug print
     printf("BitcoinMiner:\n");
-    printf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+    printf("new block found  \n  hash: %s  \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
     pblock->print();
     printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
     printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
@@ -3487,11 +3882,27 @@ void static BitcoinMiner(CWallet *pwallet)
         unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
         CBlockIndex* pindexPrev = pindexBest;
 
-        auto_ptr<CBlock> pblock(CreateNewBlock(reservekey));
+        auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
         if (!pblock.get())
             return;
+
         IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
 
+        // ppcoin: if proof-of-stake block found then process block
+        if (pblock->IsProofOfStake())
+        {
+            if (!pblock->SignBlock(*pwalletMain))
+            {
+                error("BitcoinMiner: Unable to sign new proof-of-stake block");
+                return;
+            }
+            printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); 
+            SetThreadPriority(THREAD_PRIORITY_NORMAL);
+            CheckWork(pblock.get(), *pwalletMain, reservekey);
+            SetThreadPriority(THREAD_PRIORITY_LOWEST);
+            continue;
+        }
+
         printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
 
 
@@ -3505,7 +3916,6 @@ void static BitcoinMiner(CWallet *pwallet)
         FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1);
 
         unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
-        unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8);
         unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
 
 
@@ -3536,6 +3946,11 @@ void static BitcoinMiner(CWallet *pwallet)
                     // Found a solution
                     pblock->nNonce = ByteReverse(nNonceFound);
                     assert(hash == pblock->GetHash());
+                    if (!pblock->SignBlock(*pwalletMain))
+                    {
+                        error("BitcoinMiner: Unable to sign new proof-of-work block");
+                        return;
+                    }
 
                     SetThreadPriority(THREAD_PRIORITY_NORMAL);
                     CheckWork(pblock.get(), *pwalletMain, reservekey);
@@ -3591,14 +4006,13 @@ void static BitcoinMiner(CWallet *pwallet)
                 break;
 
             // Update nTime every few seconds
+            pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+            pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime()); 
+            pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
             pblock->UpdateTime(pindexPrev);
             nBlockTime = ByteReverse(pblock->nTime);
-            if (fTestNet)
-            {
-                // Changing pblock->nTime can change work required on testnet:
-                nBlockBits = ByteReverse(pblock->nBits);
-                hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
-            }
+            if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift)
+                break;  // need to update coinbase timestamp
         }
     }
 }
index 241ef46..b1d136f 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_MAIN_H
@@ -20,6 +21,7 @@ class CBlock;
 class CBlockIndex;
 class CKeyItem;
 class CReserveKey;
+class COutPoint;
 
 class CAddress;
 class CInv;
@@ -30,9 +32,9 @@ static const unsigned int MAX_BLOCK_SIZE = 1000000;
 static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
 static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
 static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
-static const int64 MIN_TX_FEE = 50000;
+static const int64 MIN_TX_FEE = 10000;
 static const int64 MIN_RELAY_TX_FEE = 10000;
-static const int64 MAX_MONEY = 21000000 * COIN;
+static const int64 MAX_MONEY = 2000000000 * COIN;
 inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
 static const int COINBASE_MATURITY = 100;
 // Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
@@ -43,6 +45,8 @@ static const int fHaveUPnP = true;
 static const int fHaveUPnP = false;
 #endif
 
+static const uint256 hashGenesisBlockOfficial("0x000000007c82d1f0aa2896b01bf533a8cc26a1f44790be4ceb4ecde7bee24add");
+static const uint256 hashGenesisBlockTestNet("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
 
 extern CScript COINBASE_FLAGS;
 
@@ -53,11 +57,12 @@ extern CScript COINBASE_FLAGS;
 
 extern CCriticalSection cs_main;
 extern std::map<uint256, CBlockIndex*> mapBlockIndex;
+extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
 extern uint256 hashGenesisBlock;
 extern CBlockIndex* pindexGenesisBlock;
 extern int nBestHeight;
-extern CBigNum bnBestChainWork;
-extern CBigNum bnBestInvalidWork;
+extern uint64 nBestChainTrust;
+extern uint64 nBestInvalidTrust;
 extern uint256 hashBestChain;
 extern CBlockIndex* pindexBest;
 extern unsigned int nTransactionsUpdated;
@@ -69,9 +74,11 @@ extern int64 nHPSTimerStart;
 extern int64 nTimeBestReceived;
 extern CCriticalSection cs_setpwalletRegistered;
 extern std::set<CWallet*> setpwalletRegistered;
+extern std::map<uint256, CBlock*> mapOrphanBlocks;
 
 // Settings
 extern int64 nTransactionFee;
+extern int64 nBalanceReserve;
 
 
 
@@ -92,16 +99,19 @@ void PrintBlockTree();
 bool ProcessMessages(CNode* pfrom);
 bool SendMessages(CNode* pto, bool fSendTrickle);
 void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
-CBlock* CreateNewBlock(CReserveKey& reservekey);
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly=false);
 void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
 void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
 bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
 bool CheckProofOfWork(uint256 hash, unsigned int nBits);
+int64 GetProofOfStakeReward(int64 nCoinAge);
 unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
 int GetNumBlocksOfPeers();
 bool IsInitialBlockDownload();
 std::string GetWarnings(std::string strFor);
-
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew);
+uint256 WantedByOrphan(const CBlock* pblockOrphan);
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
 
 
 
@@ -339,6 +349,17 @@ public:
         return (nValue == -1);
     }
 
+    void SetEmpty()
+    {
+        nValue = 0;
+        scriptPubKey.clear();
+    }
+
+    bool IsEmpty() const
+    {
+        return (nValue == 0 && scriptPubKey.empty());
+    }
+
     uint256 GetHash() const
     {
         return SerializeHash(*this);
@@ -357,9 +378,10 @@ public:
 
     std::string ToString() const
     {
+        if (IsEmpty()) return "CTxOut(empty)";
         if (scriptPubKey.size() < 6)
             return "CTxOut(error)";
-        return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
+        return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue).c_str(), scriptPubKey.ToString().substr(0,30).c_str());
     }
 
     void print() const
@@ -387,6 +409,7 @@ class CTransaction
 {
 public:
     int nVersion;
+    unsigned int nTime;
     std::vector<CTxIn> vin;
     std::vector<CTxOut> vout;
     unsigned int nLockTime;
@@ -404,6 +427,7 @@ public:
     (
         READWRITE(this->nVersion);
         nVersion = this->nVersion;
+        READWRITE(nTime);
         READWRITE(vin);
         READWRITE(vout);
         READWRITE(nLockTime);
@@ -412,6 +436,7 @@ public:
     void SetNull()
     {
         nVersion = 1;
+        nTime = GetAdjustedTime();
         vin.clear();
         vout.clear();
         nLockTime = 0;
@@ -476,7 +501,13 @@ public:
 
     bool IsCoinBase() const
     {
-        return (vin.size() == 1 && vin[0].prevout.IsNull());
+        return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() == 1);
+    }
+
+    bool IsCoinStake() const
+    {
+        // ppcoin: the coin stake transaction is marked with the first output empty
+        return (vin.size() > 0 && vout.size() == 2 && vout[0].IsEmpty());
     }
 
     /** Check for standard transaction types
@@ -537,7 +568,7 @@ public:
         return dPriority > COIN * 144 / 250;
     }
 
-    int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const
+    int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const
     {
         // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
         int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
@@ -615,6 +646,7 @@ public:
     friend bool operator==(const CTransaction& a, const CTransaction& b)
     {
         return (a.nVersion  == b.nVersion &&
+                a.nTime     == b.nTime &&
                 a.vin       == b.vin &&
                 a.vout      == b.vout &&
                 a.nLockTime == b.nLockTime);
@@ -629,8 +661,10 @@ public:
     std::string ToString() const
     {
         std::string str;
-        str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
+        str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
+        str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
             GetHash().ToString().substr(0,10).c_str(),
+            nTime,
             nVersion,
             vin.size(),
             vout.size(),
@@ -678,12 +712,14 @@ public:
         @param[in] fStrictPayToScriptHash      true if fully validating p2sh transactions
         @return Returns true if all checks succeed
      */
-    bool ConnectInputs(MapPrevTx inputs,
+    bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
                        std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
                        const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
     bool ClientConnectInputs();
     bool CheckTransaction() const;
     bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
+    bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const;  // ppcoin: get transaction coin age
+    bool CheckProofOfStake(unsigned int nBits) const;
 
 protected:
     const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
@@ -827,6 +863,9 @@ public:
     // network and disk
     std::vector<CTransaction> vtx;
 
+    // ppcoin: block signature - signed by coin base txout[0]'s owner
+    std::vector<unsigned char> vchBlockSig;
+
     // memory only
     mutable std::vector<uint256> vMerkleTree;
 
@@ -849,11 +888,17 @@ public:
         READWRITE(nBits);
         READWRITE(nNonce);
 
-        // ConnectBlock depends on vtx being last so it can calculate offset
+        // ConnectBlock depends on vtx following header to generate CDiskTxPos
         if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
+        {
             READWRITE(vtx);
+            READWRITE(vchBlockSig);
+        }
         else if (fRead)
+        {
             const_cast<CBlock*>(this)->vtx.clear();
+            const_cast<CBlock*>(this)->vchBlockSig.clear();
+        }
     )
 
     void SetNull()
@@ -865,6 +910,7 @@ public:
         nBits = 0;
         nNonce = 0;
         vtx.clear();
+        vchBlockSig.clear();
         vMerkleTree.clear();
         nDoS = 0;
     }
@@ -886,6 +932,30 @@ public:
 
     void UpdateTime(const CBlockIndex* pindexPrev);
 
+    // ppcoin: two types of block: proof-of-work or proof-of-stake
+    bool IsProofOfStake() const
+    {
+        return (vtx.size() > 1 && vtx[1].IsCoinStake());
+    }
+
+    bool IsProofOfWork() const
+    {
+        return !IsProofOfStake();
+    }
+
+    std::pair<COutPoint, unsigned int> GetProofOfStake() const
+    {
+        return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0);
+    }
+
+    // ppcoin: get max transaction timestamp
+    int64 GetMaxTransactionTime() const
+    {
+        int64 maxTransactionTime = 0;
+        BOOST_FOREACH(const CTransaction& tx, vtx)
+            maxTransactionTime = std::max(maxTransactionTime, (int64)tx.nTime);
+        return maxTransactionTime;
+    }
 
     uint256 BuildMerkleTree() const
     {
@@ -990,7 +1060,7 @@ public:
         }
 
         // Check the header
-        if (!CheckProofOfWork(GetHash(), nBits))
+        if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
             return error("CBlock::ReadFromDisk() : errors in block header");
 
         return true;
@@ -1000,13 +1070,14 @@ public:
 
     void print() const
     {
-        printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
+        printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d, vchBlockSig=%s)\n",
             GetHash().ToString().substr(0,20).c_str(),
             nVersion,
             hashPrevBlock.ToString().substr(0,20).c_str(),
             hashMerkleRoot.ToString().substr(0,10).c_str(),
             nTime, nBits, nNonce,
-            vtx.size());
+            vtx.size(),
+            HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str());
         for (unsigned int i = 0; i < vtx.size(); i++)
         {
             printf("  ");
@@ -1026,6 +1097,9 @@ public:
     bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
     bool CheckBlock() const;
     bool AcceptBlock();
+    bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block
+    bool SignBlock(const CKeyStore& keystore);
+    bool CheckBlockSignature() const;
 
 private:
     bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
@@ -1051,8 +1125,11 @@ public:
     CBlockIndex* pnext;
     unsigned int nFile;
     unsigned int nBlockPos;
+    uint64 nChainTrust;// ppcoin: trust score of chain, in the unit of coin-days
     int nHeight;
-    CBigNum bnChainWork;
+    bool fProofOfStake; // ppcoin: is the block of proof-of-stake type
+    COutPoint prevoutStake;
+    unsigned int nStakeTime;
 
     // block header
     int nVersion;
@@ -1070,7 +1147,10 @@ public:
         nFile = 0;
         nBlockPos = 0;
         nHeight = 0;
-        bnChainWork = 0;
+        nChainTrust = 0;
+        fProofOfStake = true;
+        prevoutStake.SetNull();
+        nStakeTime = 0;
 
         nVersion       = 0;
         hashMerkleRoot = 0;
@@ -1087,7 +1167,18 @@ public:
         nFile = nFileIn;
         nBlockPos = nBlockPosIn;
         nHeight = 0;
-        bnChainWork = 0;
+        nChainTrust = 0;
+        fProofOfStake = block.IsProofOfStake();
+        if (fProofOfStake)
+        {
+            prevoutStake = block.vtx[1].vin[0].prevout;
+            nStakeTime = block.vtx[1].nTime;
+        }
+        else
+        {
+            prevoutStake.SetNull();
+            nStakeTime = 0;
+        }
 
         nVersion       = block.nVersion;
         hashMerkleRoot = block.hashMerkleRoot;
@@ -1119,13 +1210,9 @@ public:
         return (int64)nTime;
     }
 
-    CBigNum GetBlockWork() const
+    int64 GetBlockTrust() const
     {
-        CBigNum bnTarget;
-        bnTarget.SetCompact(nBits);
-        if (bnTarget <= 0)
-            return 0;
-        return (CBigNum(1)<<256) / (bnTarget+1);
+        return (nChainTrust - (pprev? pprev->nChainTrust : 0));
     }
 
     bool IsInMainChain() const
@@ -1135,7 +1222,7 @@ public:
 
     bool CheckIndex() const
     {
-        return CheckProofOfWork(GetBlockHash(), nBits);
+        return IsProofOfWork() ? CheckProofOfWork(GetBlockHash(), nBits) : true;
     }
 
     bool EraseBlockFromDisk()
@@ -1181,12 +1268,21 @@ public:
         return pindex->GetMedianTimePast();
     }
 
+    bool IsProofOfWork() const
+    {
+        return !fProofOfStake;
+    }
 
+    bool IsProofOfStake() const
+    {
+        return fProofOfStake;
+    }
 
     std::string ToString() const
     {
-        return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
-            pprev, pnext, nFile, nBlockPos, nHeight,
+        return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nChainTrust=%"PRI64d" nHeight=%d, fProofOfStake=%d prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
+            pprev, pnext, nFile, nBlockPos, nChainTrust, nHeight,
+            fProofOfStake, prevoutStake.ToString().c_str(), nStakeTime,
             hashMerkleRoot.ToString().substr(0,10).c_str(),
             GetBlockHash().ToString().substr(0,20).c_str());
     }
@@ -1226,7 +1322,19 @@ public:
         READWRITE(hashNext);
         READWRITE(nFile);
         READWRITE(nBlockPos);
+        READWRITE(nChainTrust);
         READWRITE(nHeight);
+        READWRITE(fProofOfStake);
+        if (fProofOfStake)
+        {
+            READWRITE(prevoutStake);
+            READWRITE(nStakeTime);
+        }
+        else if (fRead)
+        {
+            const_cast<CDiskBlockIndex*>(this)->prevoutStake.SetNull();
+            const_cast<CDiskBlockIndex*>(this)->nStakeTime = 0;
+        }
 
         // block header
         READWRITE(this->nVersion);
@@ -1595,7 +1703,7 @@ public:
     bool CheckSignature()
     {
         CKey key;
-        if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284")))
+        if (!key.SetPubKey(ParseHex("0487ca85b6ae9d311f996c7616d20d0c88a5b4f07d25e78f419019f35cce6522acf978b2d99f0e7a58db1f120439e5c1889266927854aa57c93956c2569188a539")))
             return error("CAlert::CheckSignature() : SetPubKey failed");
         if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
             return error("CAlert::CheckSignature() : verify signature failed");
index 37201e3..98ed2cd 100644 (file)
@@ -1,7 +1,54 @@
 # Copyright (c) 2009-2010 Satoshi Nakamoto
+# Copyright (c) 2012 The PPCoin developers
 # Distributed under the MIT/X11 software license, see the accompanying
 # file license.txt or http://www.opensource.org/licenses/mit-license.php.
 
+# Windows commandline build procedure:
+# - Install MinGW following http://www.mingw.org/wiki/Getting_Started.
+#   Install with the C++ and MSYS options checked
+#
+# - Add/edit 'MAKE' environment variable with value '/c/MinGW32/bin/mingw32-make.exe'
+#
+# - Build openssl library version: 1.0.1b
+#   download from  http://www.openssl.org/source/
+#   Extract to c:\openssl-1.0.1b-mgw
+#   In MinGW MSYS:
+#     ./config
+#     make
+#
+# - Build Berkeley DB library version: 4.8.30.NC
+#   download from  http://www.oracle.com/technology/software/products/berkeley-db/index.html
+#   Extract to c:\db-4.8.30.NC-mgw
+#   In MinGW MSYS:
+#     cd build_unix
+#     sh ../dist/configure --disable-replication --enable-mingw --enable-cxx --prefix=/usr/local
+#   Edit db.h@113 in build_unix
+#   from
+#     typedef pthread_t db_threadid_t;
+#   to 
+#     typedef u_int32_t db_threadid_t;
+#   Then
+#     make
+#
+# - Build Boost C++ library version: 1.47.0
+#   download from http://www.boost.org/users/download/
+#   Extract to c:\boost-1.47.0-mgw
+#   Install Boost.Build:
+#     cd tools\build\v2
+#     bootstrap.bat
+#     b2 install --prefix=BOOST_BUILD_INSTALL_DIR
+#   Add BOOST_BUILD_INSTALL_DIR to your PATH system environment variable
+#   Build boost library in MSDOS:
+#     cd c:\boost-1.47.0-mgw
+#     bjam toolset=gcc --build-type=complete stage
+#
+# - Build ppcoind.exe
+#   in MinGW MSYS
+#     cd ppcoin/src
+#     make ppcoind.exe -f makefile.mingw USE_UPNP=
+#
+#
+
 USE_UPNP:=0
 
 INCLUDEPATHS= \
@@ -15,10 +62,10 @@ LIBPATHS= \
  -L"C:\openssl-1.0.1b-mgw"
 
 LIBS= \
- -l boost_system-mgw45-mt-s-1_47 \
- -l boost_filesystem-mgw45-mt-s-1_47 \
- -l boost_program_options-mgw45-mt-s-1_47 \
- -l boost_thread-mgw45-mt-s-1_47 \
+ -l boost_system-mgw46-mt-s-1_47 \
+ -l boost_filesystem-mgw46-mt-s-1_47 \
+ -l boost_program_options-mgw46-mt-s-1_47 \
+ -l boost_thread-mgw46-mt-s-1_47 \
  -l db_cxx \
  -l ssl \
  -l crypto
@@ -64,12 +111,12 @@ OBJS= \
     obj/noui.o
 
 
-all: bitcoind.exe
+all: ppcoind.exe
 
 obj/%.o: %.cpp $(HEADERS)
        g++ -c $(CFLAGS) -o $@ $<
 
-bitcoind.exe: $(OBJS:obj/%=obj/%)
+ppcoind.exe: $(OBJS:obj/%=obj/%)
        g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS)
 
 TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
@@ -81,7 +128,7 @@ test_bitcoin.exe: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
        g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework $(LIBS)
 
 clean:
-       -del /Q bitcoind test_bitcoin
+       -del /Q ppcoind test_bitcoin
        -del /Q obj\*
        -del /Q obj-test\*
        -del /Q build.h
index 58331ca..cae7d44 100644 (file)
@@ -1,4 +1,5 @@
 # Copyright (c) 2009-2010 Satoshi Nakamoto
+# Copyright (c) 2011 The PPCoin developers
 # Distributed under the MIT/X11 software license, see the accompanying
 # file license.txt or http://www.opensource.org/licenses/mit-license.php.
 
@@ -109,7 +110,7 @@ OBJS= \
     obj/noui.o
 
 
-all: bitcoind
+all: ppcoind
 
 # auto-generated dependencies:
 -include obj/*.P
@@ -127,8 +128,8 @@ obj/%.o: %.cpp
              -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
          rm -f $(@:%.o=%.d)
 
-bitcoind: $(OBJS:obj/%=obj/%)
-       $(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+ppcoind: $(OBJS:obj/%=obj/%)
+       $(CXX) $(xCXXFLAGS) -rdynamic -o $@ $^ $(LDFLAGS) $(LIBS)
 
 TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
 
@@ -139,15 +140,25 @@ obj-test/%.o: test/%.cpp
              -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
          rm -f $(@:%.o=%.d)
 
-test_bitcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
+test_ppcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
        $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
 
 clean:
-       -rm -f bitcoind test_bitcoin
+       -rm -f ppcoind test_ppcoin genesis
        -rm -f obj/*.o
        -rm -f obj-test/*.o
        -rm -f obj/*.P
        -rm -f obj-test/*.P
        -rm -f src/build.h
+       -rm -f ppcoin/obj/*
+
+ppcoin/obj/genesis.o: ppcoin/genesis.cpp
+       $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
+       $(CXX) -c $(xCXXFLAGS) -MMD -DPPCOIN_GENESIS -o obj/nogui/init.o init.cpp
+
+genesis: ppcoin/obj/genesis.o $(OBJS:obj/%=obj/nogui/%)
+       $(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS)
+       -rm -f obj/init.*
+       -rm -f ppcoin/obj/genesis.*
 
 FORCE:
index d3236b3..5d5be71 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -47,6 +48,7 @@ bool fAllowDNS = false;
 static bool fUseUPnP = false;
 uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
 CAddress addrLocalHost(CService("0.0.0.0", 0), nLocalServices);
+CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices);
 static CNode* pnodeLocalHost = NULL;
 uint64 nLocalHostNonce = 0;
 array<int, THREAD_MAX> vnThreadsRunning;
@@ -248,8 +250,8 @@ bool GetMyExternalIP(CNetAddr& ipRet)
 
 void ThreadGetMyExternalIP(void* parg)
 {
-    // Wait for IRC to get it first
-    if (GetBoolArg("-irc", false))
+    // Wait for IRC to get it first - disabled with ppcoin
+    if (false && GetBoolArg("-irc", false))
     {
         for (int i = 0; i < 2 * 60; i++)
         {
@@ -977,11 +979,10 @@ void MapPort(bool /* unused fMapPort */)
 // Each pair gives a source name and a seed name.
 // The first name is used as information source for addrman.
 // The second name should resolve to a list of seed addresses.
+// testnet dns seed begins with 't', all else are ppcoin dns seeds.
 static const char *strDNSSeed[][2] = {
-    {"xf2.org", "bitseed.xf2.org"},
-    {"bluematt.me", "dnsseed.bluematt.me"},
-    {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"},
-    {"dashjr.org", "dnsseed.bitcoin.dashjr.org"},
+    {"ppcseed", "ppcseed.zapto.org"},
+    {"tncseed", "tncseed.zapto.org"},
 };
 
 void ThreadDNSAddressSeed(void* parg)
@@ -1008,11 +1009,14 @@ void ThreadDNSAddressSeed2(void* parg)
     printf("ThreadDNSAddressSeed started\n");
     int found = 0;
 
-    if (!fTestNet)
+    if (true /*!fTestNet*/)  // ppcoin enables dns seeding with testnet too
     {
         printf("Loading addresses from DNS seeds (could take a while)\n");
 
         for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
+            if (fTestNet && strDNSSeed[seed_idx][1][0] != 't') continue;
+            if ((!fTestNet) && strDNSSeed[seed_idx][1][0] == 't') continue;
+
             vector<CNetAddr> vaddr;
             vector<CAddress> vAdd;
             if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
@@ -1046,83 +1050,7 @@ void ThreadDNSAddressSeed2(void* parg)
 
 unsigned int pnSeed[] =
 {
-    0x959bd347, 0xf8de42b2, 0x73bc0518, 0xea6edc50, 0x21b00a4d, 0xc725b43d, 0xd665464d, 0x1a2a770e,
-    0x27c93946, 0x65b2fa46, 0xb80ae255, 0x66b3b446, 0xb1877a3e, 0x6ee89e3e, 0xc3175b40, 0x2a01a83c,
-    0x95b1363a, 0xa079ad3d, 0xe6ca801f, 0x027f4f4a, 0x34f7f03a, 0xf790f04a, 0x16ca801f, 0x2f4d5e40,
-    0x3a4d5e40, 0xc43a322e, 0xc8159753, 0x14d4724c, 0x7919a118, 0xe0bdb34e, 0x68a16b2e, 0xff64b44d,
-    0x6099115b, 0x9b57b05b, 0x7bd1b4ad, 0xdf95944f, 0x29d2b73d, 0xafa8db79, 0xe247ba41, 0x24078348,
-    0xf722f03c, 0x33567ebc, 0xace64ed4, 0x984d3932, 0xb5f34e55, 0x27b7024d, 0x94579247, 0x8894042e,
-    0x9357d34c, 0x1063c24b, 0xcaa228b1, 0xa3c5a8b2, 0x5dc64857, 0xa2c23643, 0xa8369a54, 0x31203077,
-    0x00707c5c, 0x09fc0b3a, 0x272e9e2e, 0xf80f043e, 0x9449ca3e, 0x5512c33e, 0xd106b555, 0xe8024157,
-    0xe288ec29, 0xc79c5461, 0xafb63932, 0xdb02ab4b, 0x0e512777, 0x8a145a4c, 0xb201ff4f, 0x5e09314b,
-    0xcd9bfbcd, 0x1c023765, 0x4394e75c, 0xa728bd4d, 0x65331552, 0xa98420b1, 0x89ecf559, 0x6e80801f,
-    0xf404f118, 0xefd62b51, 0x05918346, 0x9b186d5f, 0xacabab46, 0xf912e255, 0xc188ea62, 0xcc55734e,
-    0xc668064d, 0xd77a4558, 0x46201c55, 0xf17dfc80, 0xf7142f2e, 0x87bfb718, 0x8aa54fb2, 0xc451d518,
-    0xc4ae8831, 0x8dd44d55, 0x5bbd206c, 0x64536b5d, 0x5c667e60, 0x3b064242, 0xfe963a42, 0xa28e6dc8,
-    0xe8a9604a, 0xc989464e, 0xd124a659, 0x50065140, 0xa44dfe5e, 0x1079e655, 0x3fb986d5, 0x47895b18,
-    0x7d3ce4ad, 0x4561ba50, 0x296eec62, 0x255b41ad, 0xaed35ec9, 0x55556f12, 0xc7d3154d, 0x3297b65d,
-    0x8930121f, 0xabf42e4e, 0x4a29e044, 0x1212685d, 0x676c1e40, 0xce009744, 0x383a8948, 0xa2dbd0ad,
-    0xecc2564d, 0x07dbc252, 0x887ee24b, 0x5171644c, 0x6bb798c1, 0x847f495d, 0x4cbb7145, 0x3bb81c32,
-    0x45eb262e, 0xc8015a4e, 0x250a361b, 0xf694f946, 0xd64a183e, 0xd4f1dd59, 0x8f20ffd4, 0x51d9e55c,
-    0x09521763, 0x5e02002e, 0x32c8074d, 0xe685762e, 0x8290b0bc, 0x762a922e, 0xfc5ee754, 0x83a24829,
-    0x775b224d, 0x6295bb4d, 0x38ec0555, 0xbffbba50, 0xe5560260, 0x86b16a7c, 0xd372234e, 0x49a3c24b,
-    0x2f6a171f, 0x4d75ed60, 0xae94115b, 0xcb543744, 0x63080c59, 0x3f9c724c, 0xc977ce18, 0x532efb18,
-    0x69dc3b2e, 0x5f94d929, 0x1732bb4d, 0x9c814b4d, 0xe6b3762e, 0xc024f662, 0x8face35b, 0x6b5b044d,
-    0x798c7b57, 0x79a6b44c, 0x067d3057, 0xf9e94e5f, 0x91cbe15b, 0x71405eb2, 0x2662234e, 0xcbcc4a6d,
-    0xbf69d54b, 0xa79b4e55, 0xec6d3e51, 0x7c0b3c02, 0x60f83653, 0x24c1e15c, 0x1110b62e, 0x10350f59,
-    0xa56f1d55, 0x3509e7a9, 0xeb128354, 0x14268e2e, 0x934e28bc, 0x8e32692e, 0x8331a21f, 0x3e633932,
-    0xc812b12e, 0xc684bf2e, 0x80112d2e, 0xe0ddc96c, 0xc630ca4a, 0x5c09b3b2, 0x0b580518, 0xc8e9d54b,
-    0xd169aa43, 0x17d0d655, 0x1d029963, 0x7ff87559, 0xcb701f1f, 0x6fa3e85d, 0xe45e9a54, 0xf05d1802,
-    0x44d03b2e, 0x837b692e, 0xccd4354e, 0x3d6da13c, 0x3423084d, 0xf707c34a, 0x55f6db3a, 0xad26e442,
-    0x6233a21f, 0x09e80e59, 0x8caeb54d, 0xbe870941, 0xb407d20e, 0x20b51018, 0x56fb152e, 0x460d2a4e,
-    0xbb9a2946, 0x560eb12e, 0xed83dd29, 0xd6724f53, 0xa50aafb8, 0x451346d9, 0x88348e2e, 0x7312fead,
-    0x8ecaf96f, 0x1bda4e5f, 0xf1671e40, 0x3c8c3e3b, 0x4716324d, 0xdde24ede, 0xf98cd17d, 0xa91d4644,
-    0x28124eb2, 0x147d5129, 0xd022042e, 0x61733d3b, 0xad0d5e02, 0x8ce2932e, 0xe5c18502, 0x549c1e32,
-    0x9685801f, 0x86e217ad, 0xd948214b, 0x4110f462, 0x3a2e894e, 0xbd35492e, 0x87e0d558, 0x64b8ef7d,
-    0x7c3eb962, 0x72a84b3e, 0x7cd667c9, 0x28370a2e, 0x4bc60e7b, 0x6fc1ec60, 0x14a6983f, 0x86739a4b,
-    0x46954e5f, 0x32e2e15c, 0x2e9326cf, 0xe5801c5e, 0x379607b2, 0x32151145, 0xf0e39744, 0xacb54c55,
-    0xa37dfb60, 0x83b55cc9, 0x388f7ca5, 0x15034f5f, 0x3e94965b, 0x68e0ffad, 0x35280f59, 0x8fe190cf,
-    0x7c6ba5b2, 0xa5e9db43, 0x4ee1fc60, 0xd9d94e5f, 0x04040677, 0x0ea9b35e, 0x5961f14f, 0x67fda063,
-    0xa48a5a31, 0xc6524e55, 0x283d325e, 0x3f37515f, 0x96b94b3e, 0xacce620e, 0x6481cc5b, 0xa4a06d4b,
-    0x9e95d2d9, 0xe40c03d5, 0xc2f4514b, 0xb79aad44, 0xf64be843, 0xb2064070, 0xfca00455, 0x429dfa4e,
-    0x2323f173, 0xeda4185e, 0xabd5227d, 0x9efd4d58, 0xb1104758, 0x4811e955, 0xbd9ab355, 0xe921f44b,
-    0x9f166dce, 0x09e279b2, 0xe0c9ac7b, 0x7901a5ad, 0xa145d4b0, 0x79104671, 0xec31e35a, 0x4fe0b555,
-    0xc7d9cbad, 0xad057f55, 0xe94cc759, 0x7fe0b043, 0xe4529f2e, 0x0d4dd4b2, 0x9f11a54d, 0x031e2e4e,
-    0xe6014f5f, 0x11d1ca6c, 0x26bd7f61, 0xeb86854f, 0x4d347b57, 0x116bbe2e, 0xdba7234e, 0x7bcbfd2e,
-    0x174dd4b2, 0x6686762e, 0xb089ba50, 0xc6258246, 0x087e767b, 0xc4a8cb4a, 0x595dba50, 0x7f0ae502,
-    0x7b1dbd5a, 0xa0603492, 0x57d1af4b, 0x9e21ffd4, 0x6393064d, 0x7407376e, 0xe484762e, 0x122a4e53,
-    0x4a37aa43, 0x3888a6be, 0xee77864e, 0x039c8dd5, 0x688d89af, 0x0e988f62, 0x08218246, 0xfc2f8246,
-    0xd1d97040, 0xd64cd4b2, 0x5ae4a6b8, 0x7d0de9bc, 0x8d304d61, 0x06c5c672, 0xa4c8bd4d, 0xe0fd373b,
-    0x575ebe4d, 0x72d26277, 0x55570f55, 0x77b154d9, 0xe214293a, 0xfc740f4b, 0xfe3f6a57, 0xa9c55f02,
-    0xae4054db, 0x2394d918, 0xb511b24a, 0xb8741ab2, 0x0758e65e, 0xc7b5795b, 0xb0a30a4c, 0xaf7f170c,
-    0xf3b4762e, 0x8179576d, 0x738a1581, 0x4b95b64c, 0x9829b618, 0x1bea932e, 0x7bdeaa4b, 0xcb5e0281,
-    0x65618f54, 0x0658474b, 0x27066acf, 0x40556d65, 0x7d204d53, 0xf28bc244, 0xdce23455, 0xadc0ff54,
-    0x3863c948, 0xcee34e5f, 0xdeb85e02, 0x2ed17a61, 0x6a7b094d, 0x7f0cfc40, 0x59603f54, 0x3220afbc,
-    0xb5dfd962, 0x125d21c0, 0x13f8d243, 0xacfefb4e, 0x86c2c147, 0x3d8bbd59, 0xbd02a21f, 0x2593042e,
-    0xc6a17a7c, 0x28925861, 0xb487ed44, 0xb5f4fd6d, 0x90c28a45, 0x5a14f74d, 0x43d71b4c, 0x728ebb5d,
-    0x885bf950, 0x08134dd0, 0x38ec046e, 0xc575684b, 0x50082d2e, 0xa2f47757, 0x270f86ae, 0xf3ff6462,
-    0x10ed3f4e, 0x4b58d462, 0xe01ce23e, 0x8c5b092e, 0x63e52f4e, 0x22c1e85d, 0xa908f54e, 0x8591624f,
-    0x2c0fb94e, 0xa280ba3c, 0xb6f41b4c, 0x24f9aa47, 0x27201647, 0x3a3ea6dc, 0xa14fc3be, 0x3c34bdd5,
-    0x5b8d4f5b, 0xaadeaf4b, 0xc71cab50, 0x15697a4c, 0x9a1a734c, 0x2a037d81, 0x2590bd59, 0x48ec2741,
-    0x53489c5b, 0x7f00314b, 0x2170d362, 0xf2e92542, 0x42c10b44, 0x98f0f118, 0x883a3456, 0x099a932e,
-    0xea38f7bc, 0x644e9247, 0xbb61b62e, 0x30e0863d, 0x5f51be54, 0x207215c7, 0x5f306c45, 0xaa7f3932,
-    0x98da7d45, 0x4e339b59, 0x2e411581, 0xa808f618, 0xad2c0c59, 0x54476741, 0x09e99fd1, 0x5db8f752,
-    0xc16df8bd, 0x1dd4b44f, 0x106edf2e, 0x9e15c180, 0x2ad6b56f, 0x633a5332, 0xff33787c, 0x077cb545,
-    0x6610be6d, 0x75aad2c4, 0x72fb4d5b, 0xe81e0f59, 0x576f6332, 0x47333373, 0x351ed783, 0x2d90fb50,
-    0x8d5e0f6c, 0x5b27a552, 0xdb293ebb, 0xe55ef950, 0x4b133ad8, 0x75df975a, 0x7b6a8740, 0xa899464b,
-    0xfab15161, 0x10f8b64d, 0xd055ea4d, 0xee8e146b, 0x4b14afb8, 0x4bc1c44a, 0x9b961dcc, 0xd111ff43,
-    0xfca0b745, 0xc800e412, 0x0afad9d1, 0xf751c350, 0xf9f0cccf, 0xa290a545, 0x8ef13763, 0x7ec70d59,
-    0x2b066acf, 0x65496c45, 0xade02c1b, 0xae6eb077, 0x92c1e65b, 0xc064e6a9, 0xc649e56d, 0x5287a243,
-    0x36de4f5b, 0x5b1df6ad, 0x65c39a59, 0xdba805b2, 0x20067aa8, 0x6457e56d, 0x3cee26cf, 0xfd3ff26d,
-    0x04f86d4a, 0x06b8e048, 0xa93bcd5c, 0x91135852, 0xbe90a643, 0x8fa0094d, 0x06d8215f, 0x2677094d,
-    0xd735685c, 0x164a00c9, 0x5209ac5f, 0xa9564c5c, 0x3b504f5f, 0xcc826bd0, 0x4615042e, 0x5fe13b4a,
-    0x8c81b86d, 0x879ab68c, 0x1de564b8, 0x434487d8, 0x2dcb1b63, 0x82ab524a, 0xb0676abb, 0xa13d9c62,
-    0xdbb5b86d, 0x5b7f4b59, 0xaddfb44d, 0xad773532, 0x3997054c, 0x72cebd89, 0xb194544c, 0xc5b8046e,
-    0x6e1adeb2, 0xaa5abb51, 0xefb54b44, 0x15efc54f, 0xe9f1bc4d, 0x5f401b6c, 0x97f018ad, 0xc82f9252,
-    0x2cdc762e, 0x8e52e56d, 0x1827175e, 0x9b7d7d80, 0xb2ad6845, 0x51065140, 0x71180a18, 0x5b27006c,
-    0x0621e255, 0x721cbe58, 0x670c0cb8, 0xf8bd715d, 0xe0bdc5d9, 0xed843501, 0x4b84554d, 0x7f1a18bc,
-    0x53bcaf47, 0x5729d35f, 0xf0dda246, 0x22382bd0, 0x4d641fb0, 0x316afcde, 0x50a22f1f, 0x73608046,
-    0xc461d84a, 0xb2dbe247,
+    0xfc01a8c0,
 };
 
 void DumpAddresses()
@@ -1670,8 +1598,10 @@ void StartNode(void* parg)
         MapPort(fUseUPnP);
 
     // Get addresses from IRC and advertise ours
-    if (!CreateThread(ThreadIRCSeed, NULL))
-        printf("Error: CreateThread(ThreadIRCSeed) failed\n");
+    // if (!CreateThread(ThreadIRCSeed, NULL))
+    //     printf("Error: CreateThread(ThreadIRCSeed) failed\n");
+    // IRC disabled with ppcoin
+    printf("IRC seeding/communication disabled\n");
 
     // Send and receive from sockets, accept connections
     if (!CreateThread(ThreadSocketHandler, NULL))
index 65118a4..c0cda53 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_NET_H
@@ -86,6 +87,7 @@ extern bool fClient;
 extern bool fAllowDNS;
 extern uint64 nLocalServices;
 extern CAddress addrLocalHost;
+extern CAddress addrSeenByPeer;
 extern uint64 nLocalHostNonce;
 extern boost::array<int, THREAD_MAX> vnThreadsRunning;
 extern CAddrMan addrman;
@@ -151,6 +153,7 @@ public:
     std::set<CAddress> setAddrKnown;
     bool fGetAddr;
     std::set<uint256> setKnown;
+    uint256 hashCheckpointKnown; // ppcoin: known sent sync-checkpoint
 
     // inventory based relay
     mruset<CInv> setInventoryKnown;
@@ -185,6 +188,7 @@ public:
         nStartingHeight = -1;
         fGetAddr = false;
         nMisbehavior = 0;
+        hashCheckpointKnown = 0;
         setInventoryKnown.max_size(SendBufferSize() / 1000);
 
         // Be shy and don't send version until we hear
diff --git a/src/ppcoin/genesis.cpp b/src/ppcoin/genesis.cpp
new file mode 100644 (file)
index 0000000..9609161
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The PPCoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+#include "../headers.h"
+#include "../db.h"
+#include "../net.h"
+#include "../init.h"
+#include "../main.h"
+#include "../util.h"
+
+using namespace std;
+using namespace boost;
+
+int main(int argc, char *argv[])
+{
+    fPrintToConsole = true;
+    printf("PPCoin Begin Genesis Block\n");
+
+    // Genesis block
+    const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
+    CTransaction txNew;
+    txNew.vin.resize(1);
+    txNew.vout.resize(1);
+    txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
+    txNew.vout[0].SetEmpty();
+    CBlock block;
+    block.vtx.push_back(txNew);
+    block.hashPrevBlock = 0;
+    block.hashMerkleRoot = block.BuildMerkleTree();
+    block.nVersion = 1;
+    block.nBits    = 0x1d00ffff;
+    block.nTime    = GetAdjustedTime();
+    block.nNonce   = 0;
+
+    CBigNum bnTarget;
+    bnTarget.SetCompact(block.nBits);
+
+    while (block.GetHash() > bnTarget.getuint256())
+    {
+        if ((block.nNonce >> 20) << 20 == block.nNonce)
+        {
+            if (block.vtx[0].nTime + 7200 < GetAdjustedTime() + 60)
+                block.vtx[0].nTime = GetAdjustedTime();
+            block.nTime = GetAdjustedTime();
+            printf("n=%dM hash=%s\n", block.nNonce >> 20,
+                   block.GetHash().ToString().c_str());
+        }
+        block.nNonce++;
+    }
+
+    printf("PPCoin Found Genesis Block:\n");
+    printf("genesis hash=%s\n", block.GetHash().ToString().c_str());
+    printf("merkle  root=%s\n", block.hashMerkleRoot.ToString().c_str());
+    block.print();
+
+    printf("PPCoin End Genesis Block\n");
+}
diff --git a/src/ppcoin/obj/.gitignore b/src/ppcoin/obj/.gitignore
new file mode 100644 (file)
index 0000000..d6b7ef3
--- /dev/null
@@ -0,0 +1,2 @@
+*
+!.gitignore
index b516f1b..30f9714 100644 (file)
 #include <string>
 #include "uint256.h"
 
+#define PPCOIN_PORT  9901
+#define RPC_PORT     9902
+#define TESTNET_PORT 9903
+
 extern bool fTestNet;
+
 static inline unsigned short GetDefaultPort(const bool testnet = fTestNet)
 {
-    return testnet ? 18333 : 8333;
+    return testnet ? TESTNET_PORT : PPCOIN_PORT;
 }
 
 
index 9a9a489..1356a16 100644 (file)
@@ -34,9 +34,9 @@ QString BitcoinUnits::name(int unit)
 {
     switch(unit)
     {
-    case BTC: return QString("BTC");
-    case mBTC: return QString("mBTC");
-    case uBTC: return QString::fromUtf8("μBTC");
+    case BTC: return QString("PPC");
+    case mBTC: return QString("mPPC");
+    case uBTC: return QString::fromUtf8("μPPC");
     default: return QString("???");
     }
 }
@@ -45,9 +45,9 @@ QString BitcoinUnits::description(int unit)
 {
     switch(unit)
     {
-    case BTC: return QString("Bitcoins");
-    case mBTC: return QString("Milli-Bitcoins (1 / 1,000)");
-    case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)");
+    case BTC: return QString("PPCoins");
+    case mBTC: return QString("Milli-PPCoins (1 / 1,000)");
+    case uBTC: return QString("Micro-PPCoins (1 / 1,000,000)");
     default: return QString("???");
     }
 }
@@ -56,10 +56,10 @@ qint64 BitcoinUnits::factor(int unit)
 {
     switch(unit)
     {
-    case BTC:  return 100000000;
-    case mBTC: return 100000;
-    case uBTC: return 100;
-    default:   return 100000000;
+    case BTC:  return 1000000;
+    case mBTC: return 1000;
+    case uBTC: return 1;
+    default:   return 1000000;
     }
 }
 
@@ -67,9 +67,9 @@ int BitcoinUnits::amountDigits(int unit)
 {
     switch(unit)
     {
-    case BTC: return 8; // 21,000,000 (# digits, without commas)
-    case mBTC: return 11; // 21,000,000,000
-    case uBTC: return 14; // 21,000,000,000,000
+    case BTC: return 10; // 21,000,000 (# digits, without commas)
+    case mBTC: return 13; // 21,000,000,000
+    case uBTC: return 16; // 21,000,000,000,000
     default: return 0;
     }
 }
@@ -78,9 +78,9 @@ int BitcoinUnits::decimals(int unit)
 {
     switch(unit)
     {
-    case BTC: return 8;
-    case mBTC: return 5;
-    case uBTC: return 2;
+    case BTC: return 6;
+    case mBTC: return 3;
+    case uBTC: return 0;
     default: return 0;
     }
 }
index ccb19c3..a3ab8e1 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #include <boost/foreach.hpp>
@@ -19,7 +20,6 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
 
 
 
-typedef vector<unsigned char> valtype;
 static const valtype vchFalse(0);
 static const valtype vchZero(0);
 static const valtype vchTrue(1, 1);
@@ -1584,6 +1584,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
     assert(nIn < txTo.vin.size());
     CTxIn& txin = txTo.vin[nIn];
     assert(txin.prevout.n < txFrom.vout.size());
+    assert(txin.prevout.hash == txFrom.GetHash());
     const CTxOut& txout = txFrom.vout[txin.prevout.n];
 
     // Leave out the signature from the hash, since a signature can't sign itself.
index 5397a19..2aa08fe 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef H_BITCOIN_SCRIPT
@@ -12,6 +13,8 @@
 
 #include <boost/foreach.hpp>
 
+typedef std::vector<unsigned char> valtype;
+
 class CTransaction;
 class CKeyStore;
 
index 349a40b..ca12051 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_SERIALIZE_H
@@ -155,8 +156,14 @@ template<typename Stream> inline void Unserialize(Stream& s, bool& a, int, int=0
 
 
 
-
-
+#ifndef THROW_WITH_STACKTRACE
+#define THROW_WITH_STACKTRACE(exception)  \
+{                                         \
+    LogStackTrace();                      \
+    throw (exception);                    \
+}
+void LogStackTrace();
+#endif
 
 //
 // Compact size
@@ -234,7 +241,7 @@ uint64 ReadCompactSize(Stream& is)
         nSizeRet = xSize;
     }
     if (nSizeRet > (uint64)MAX_SIZE)
-        throw std::ios_base::failure("ReadCompactSize() : size too large");
+        THROW_WITH_STACKTRACE(std::ios_base::failure("ReadCompactSize() : size too large"));
     return nSizeRet;
 }
 
@@ -905,7 +912,7 @@ public:
     {
         state |= bits;
         if (state & exceptmask)
-            throw std::ios_base::failure(psz);
+            THROW_WITH_STACKTRACE(std::ios_base::failure(psz));
     }
 
     bool eof() const             { return size() == 0; }
@@ -1120,7 +1127,7 @@ public:
     {
         state |= bits;
         if (state & exceptmask)
-            throw std::ios_base::failure(psz);
+            THROW_WITH_STACKTRACE(std::ios_base::failure(psz));
     }
 
     bool fail() const            { return state & (std::ios::badbit | std::ios::failbit); }
index a407714..89509b1 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
@@ -53,6 +54,10 @@ namespace boost {
 #include "shlwapi.h"
 #endif
 
+#ifndef WIN32
+#include <execinfo.h>
+#endif
+
 using namespace std;
 using namespace boost;
 
@@ -189,7 +194,7 @@ uint256 GetRandHash()
 
 
 
-
+static FILE* fileout = NULL;
 
 inline int OutputDebugStringF(const char* pszFormat, ...)
 {
@@ -205,8 +210,6 @@ inline int OutputDebugStringF(const char* pszFormat, ...)
     else
     {
         // print to debug.log
-        static FILE* fileout = NULL;
-
         if (!fileout)
         {
             boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
@@ -371,7 +374,7 @@ string FormatMoney(int64 n, bool fPlus)
     int64 n_abs = (n > 0 ? n : -n);
     int64 quotient = n_abs/COIN;
     int64 remainder = n_abs%COIN;
-    string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder);
+    string str = strprintf("%"PRI64d".%06"PRI64d, quotient, remainder);
 
     // Right-trim excess 0's before the decimal point:
     int nTrim = 0;
@@ -801,6 +804,19 @@ void PrintException(std::exception* pex, const char* pszThread)
     throw;
 }
 
+void LogStackTrace() {
+    printf("\n\n******* exception encountered *******\n");
+    if (fileout)
+    {
+#ifndef WIN32
+        void* pszBuffer[32];
+        size_t size;
+        size = backtrace(pszBuffer, 32);
+        backtrace_symbols_fd(pszBuffer, size, fileno(fileout));
+#endif
+    }
+}
+
 void PrintExceptionContinue(std::exception* pex, const char* pszThread)
 {
     char pszMessage[10000];
@@ -836,12 +852,12 @@ boost::filesystem::path GetDefaultDataDir()
 {
     namespace fs = boost::filesystem;
 
-    // Windows: C:\Documents and Settings\username\Application Data\Bitcoin
-    // Mac: ~/Library/Application Support/Bitcoin
-    // Unix: ~/.bitcoin
+    // Windows: C:\Documents and Settings\username\Application Data\PPCoin
+    // Mac: ~/Library/Application Support/PPCoin
+    // Unix: ~/.ppcoin
 #ifdef WIN32
     // Windows
-    return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "Bitcoin";
+    return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "PPCoin";
 #else
     fs::path pathRet;
     char* pszHome = getenv("HOME");
@@ -853,10 +869,10 @@ boost::filesystem::path GetDefaultDataDir()
     // Mac
     pathRet /= "Library/Application Support";
     fs::create_directory(pathRet);
-    return pathRet / "Bitcoin";
+    return pathRet / "PPCoin";
 #else
     // Unix
-    return pathRet / ".bitcoin";
+    return pathRet / ".ppcoin";
 #endif
 #endif
 }
@@ -900,7 +916,7 @@ boost::filesystem::path GetConfigFile()
 {
     namespace fs = boost::filesystem;
 
-    fs::path pathConfigFile(GetArg("-conf", "bitcoin.conf"));
+    fs::path pathConfigFile(GetArg("-conf", "ppcoin.conf"));
     if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile;
     return pathConfigFile;
 }
@@ -936,7 +952,7 @@ boost::filesystem::path GetPidFile()
 {
     namespace fs = boost::filesystem;
 
-    fs::path pathPidFile(GetArg("-pid", "bitcoind.pid"));
+    fs::path pathPidFile(GetArg("-pid", "ppcoind.pid"));
     if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
     return pathPidFile;
 }
@@ -1203,6 +1219,7 @@ bool GetStartOnSystemStartup()
         if (line.find("Hidden") != string::npos &&
             line.find("true") != string::npos)
             return false;
+>>>>>>> bitcoin
     }
     optionFile.close();
 
index ef15260..dc40af5 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_UTIL_H
@@ -36,8 +37,8 @@ typedef int pid_t; /* define for windows compatiblity */
 typedef long long  int64;
 typedef unsigned long long  uint64;
 
-static const int64 COIN = 100000000;
-static const int64 CENT = 1000000;
+static const int64 COIN = 1000000;
+static const int64 CENT = 10000;
 
 #define loop                for (;;)
 #define BEGIN(a)            ((char*)&(a))
@@ -103,8 +104,14 @@ inline void Sleep(int64 n)
 }
 #endif
 
-
-
+#ifndef THROW_WITH_STACKTRACE
+#define THROW_WITH_STACKTRACE(exception)  \
+{                                         \
+    LogStackTrace();                      \
+    throw (exception);                    \
+}
+void LogStackTrace();
+#endif
 
 
 
@@ -661,9 +668,9 @@ public:
 // Note: It turns out we might have been able to use boost::thread
 // by using TerminateThread(boost::thread.native_handle(), 0);
 #ifdef WIN32
-typedef HANDLE pthread_t;
+typedef HANDLE bitcoin_pthread_t;
 
-inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false)
+inline bitcoin_pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false)
 {
     DWORD nUnused = 0;
     HANDLE hthread =
@@ -677,12 +684,12 @@ inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=fa
     if (hthread == NULL)
     {
         printf("Error: CreateThread() returned %d\n", GetLastError());
-        return (pthread_t)0;
+        return (bitcoin_pthread_t)0;
     }
     if (!fWantHandle)
     {
         CloseHandle(hthread);
-        return (pthread_t)-1;
+        return (bitcoin_pthread_t)-1;
     }
     return hthread;
 }
index 60b7aae..3ae1784 100644 (file)
@@ -49,9 +49,9 @@ const std::string CLIENT_NAME("Satoshi");
 
 #ifndef BUILD_DESC
 #    ifdef GIT_COMMIT_ID
-#        define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID)
+#        define BUILD_DESC BUILD_DESC_FROM_COMMIT(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD, GIT_COMMIT_ID)
 #    else
-#        define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD)
+#        define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD)
 #    endif
 #endif
 
index 9718e75..29efa06 100644 (file)
@@ -26,6 +26,12 @@ extern const std::string CLIENT_NAME;
 extern const std::string CLIENT_BUILD;
 extern const std::string CLIENT_DATE;
 
+// ppcoin version - intended for display purpose ONLY
+#define PPCOIN_VERSION_MAJOR       0
+#define PPCOIN_VERSION_MINOR       1
+#define PPCOIN_VERSION_REVISION    0
+#define PPCOIN_VERSION_BUILD       0
+
 //
 // network protocol versioning
 //
@@ -33,7 +39,7 @@ extern const std::string CLIENT_DATE;
 static const int PROTOCOL_VERSION = 60001;
 
 // earlier versions not supported as of Feb 2012, and are disconnected
-static const int MIN_PROTO_VERSION = 209;
+static const int MIN_PROTO_VERSION = 60001;
 
 // nTime field added to CAddress, starting with this version;
 // if possible, avoid requesting addresses nodes older than this
index d7a70fe..8acc825 100644 (file)
@@ -1,11 +1,13 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #include "wallet.h"
 #include "walletdb.h"
 #include "crypter.h"
+#include "checkpoints.h"
 #include "ui_interface.h"
 
 using namespace std;
@@ -69,6 +71,10 @@ bool CWallet::AddCScript(const CScript& redeemScript)
     return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
 }
 
+// ppcoin: optional setting to create coinstake only when unlocked;
+//         serves to disable the trivial sendmoney when OS account compromised
+bool fWalletUnlockStakeOnly = false;
+
 bool CWallet::Unlock(const SecureString& strWalletPassphrase)
 {
     if (!IsLocked())
@@ -294,7 +300,7 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx)
                 CWalletTx& wtx = (*mi).second;
                 if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
                 {
-                    printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+                    printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
                     wtx.MarkSpent(txin.prevout.n);
                     wtx.WriteToDisk();
                     vWalletUpdated.push_back(txin.prevout.hash);
@@ -485,7 +491,7 @@ int CWalletTx::GetRequestCount() const
     int nRequests = -1;
     {
         LOCK(pwallet->cs_wallet);
-        if (IsCoinBase())
+        if (IsCoinBase() || IsCoinStake())
         {
             // Generated block
             if (hashBlock != 0)
@@ -526,7 +532,7 @@ void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, l
     listSent.clear();
     strSentAccount = strFromAccount;
 
-    if (IsCoinBase())
+    if (IsCoinBase() || IsCoinStake())
     {
         if (GetBlocksToMaturity() > 0)
             nGeneratedImmature = pwallet->GetCredit(*this);
@@ -717,7 +723,7 @@ void CWallet::ReacceptWalletTransactions()
         BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
         {
             CWalletTx& wtx = item.second;
-            if (wtx.IsCoinBase() && wtx.IsSpent(0))
+            if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
                 continue;
 
             CTxIndex txindex;
@@ -743,7 +749,7 @@ void CWallet::ReacceptWalletTransactions()
                 }
                 if (fUpdated)
                 {
-                    printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+                    printf("ReacceptWalletTransactions found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
                     wtx.MarkDirty();
                     wtx.WriteToDisk();
                 }
@@ -751,7 +757,7 @@ void CWallet::ReacceptWalletTransactions()
             else
             {
                 // Reaccept any txes of ours that aren't already in a block
-                if (!wtx.IsCoinBase())
+                if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
                     wtx.AcceptWalletTransaction(txdb, false);
             }
         }
@@ -768,14 +774,14 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
 {
     BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
     {
-        if (!tx.IsCoinBase())
+        if (!(tx.IsCoinBase() || tx.IsCoinStake()))
         {
             uint256 hash = tx.GetHash();
             if (!txdb.ContainsTx(hash))
                 RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
         }
     }
-    if (!IsCoinBase())
+    if (!(IsCoinBase() || IsCoinStake()))
     {
         uint256 hash = GetHash();
         if (!txdb.ContainsTx(hash))
@@ -877,7 +883,35 @@ int64 CWallet::GetUnconfirmedBalance() const
     return nTotal;
 }
 
-bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+// ppcoin: total coins staked (non-spendable until maturity)
+int64 CWallet::GetStake() const
+{
+    int64 nTotal = 0;
+    LOCK(cs_wallet);
+    for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+    {
+        const CWalletTx* pcoin = &(*it).second;
+        if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+            nTotal += CWallet::GetCredit(*pcoin);
+    }
+    return nTotal;
+}
+
+int64 CWallet::GetNewMint() const
+{
+    int64 nTotal = 0;
+    LOCK(cs_wallet);
+    for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+    {
+        const CWalletTx* pcoin = &(*it).second;
+        if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+            nTotal += CWallet::GetCredit(*pcoin);
+    }
+    return nTotal;
+}
+
+
+bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
 {
     setCoinsRet.clear();
     nValueRet = 0;
@@ -902,7 +936,7 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
             if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
                 continue;
 
-            if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+            if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
                 continue;
 
             int nDepth = pcoin->GetDepthInMainChain();
@@ -914,6 +948,9 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
                 if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
                     continue;
 
+                if (pcoin->nTime > nSpendTime)
+                    continue;  // ppcoin: timestamp must not exceed spend time
+
                 int64 n = pcoin->vout[i].nValue;
 
                 if (n <= 0)
@@ -1022,11 +1059,11 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
     return true;
 }
 
-bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
 {
-    return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
-            SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
-            SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
+    return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) ||
+            SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) ||
+            SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet));
 }
 
 
@@ -1067,7 +1104,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
                 // Choose coins to use
                 set<pair<const CWalletTx*,unsigned int> > setCoins;
                 int64 nValueIn = 0;
-                if (!SelectCoins(nTotalValue, setCoins, nValueIn))
+                if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
                     return false;
                 BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
                 {
@@ -1130,8 +1167,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
 
                 // Check that enough fee is included
                 int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
-                bool fAllowFree = CTransaction::AllowFree(dPriority);
-                int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND);
+                int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
                 if (nFeeRet < max(nPayFee, nMinFee))
                 {
                     nFeeRet = max(nPayFee, nMinFee);
@@ -1156,6 +1192,94 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w
     return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
 }
 
+// ppcoin: create coin stake transaction
+bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew)
+{
+    CBigNum bnTargetPerCoinDay;
+    bnTargetPerCoinDay.SetCompact(nBits);
+
+    LOCK2(cs_main, cs_wallet);
+    txNew.vin.clear();
+    txNew.vout.clear();
+    // Mark coin stake transaction
+    CScript scriptEmpty;
+    scriptEmpty.clear();
+    txNew.vout.push_back(CTxOut(0, scriptEmpty));
+    // Choose coins to use
+    int64 nBalance = GetBalance();
+    if (nBalance <= nBalanceReserve)
+        return false;
+    set<pair<const CWalletTx*,unsigned int> > setCoins;
+    vector<const CWalletTx*> vwtxPrev;
+    int64 nValueIn = 0;
+    if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn))
+        return false;
+    if (setCoins.empty())
+        return false;
+    int64 nCredit = 0;
+    BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+    {
+        CTxDB txdb("r");
+        CTxIndex txindex;
+        if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+            continue;
+
+        // Read block header
+        CBlock block;
+        if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+            continue;
+        if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime)
+            continue; // only count coins meeting min age requirement
+
+        int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
+        CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60);
+        // Calculate hash
+        CDataStream ss(SER_GETHASH, 0);
+        ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime;
+        if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+        {
+            txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+            nCredit += pcoin.first->vout[pcoin.second].nValue;
+            vwtxPrev.push_back(pcoin.first);
+            // Set output scriptPubKey
+            txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey));
+            break;
+        }
+    }
+    if (nCredit == 0 || nCredit > nBalance - nBalanceReserve)
+        return false;
+    BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+    {
+        if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
+        {
+            if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve)
+                break;
+            txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+            nCredit += pcoin.first->vout[pcoin.second].nValue;
+            vwtxPrev.push_back(pcoin.first);
+        }
+    }
+    // Calculate coin age reward
+    {
+        uint64 nCoinAge;
+        CTxDB txdb("r");
+        if (!txNew.GetCoinAge(txdb, nCoinAge))
+            return error("CreateCoinStake : failed to calculate coin age");
+        nCredit += GetProofOfStakeReward(nCoinAge);
+    }
+    // Set output amount
+    txNew.vout[1].nValue = nCredit;
+
+    // Sign
+    int nIn = 0;
+    BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
+    {
+        if (!SignSignature(*this, *pcoin, txNew, nIn++))
+            return error("CreateCoinStake : failed to sign coinstake");
+    }
+    return true;
+}
+
 // Call after CreateTransaction unless you want to abort
 bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
 {
@@ -1220,6 +1344,12 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew,
         printf("SendMoney() : %s", strError.c_str());
         return strError;
     }
+    if (fWalletUnlockStakeOnly)
+    {
+        string strError = _("Error: Wallet unlocked for coinstake only, unable to create transaction.");
+        printf("SendMoney() : %s", strError.c_str());
+        return strError;
+    }
     if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
     {
         string strError;
@@ -1311,10 +1441,15 @@ void CWallet::PrintWallet(const CBlock& block)
 {
     {
         LOCK(cs_wallet);
-        if (mapWallet.count(block.vtx[0].GetHash()))
+        if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash()))
         {
             CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
-            printf("    mine:  %d  %d  %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+            printf("    mine:  %d  %d  %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
+        }
+        if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash()))
+        {
+            CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()];
+            printf("    stake: %d  %d  %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
         }
     }
     printf("\n");
@@ -1505,6 +1640,107 @@ int64 CWallet::GetOldestKeyPoolTime()
     return keypool.nTime;
 }
 
+// ppcoin: check 'spent' consistency between wallet and txindex
+bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+    nMismatchFound = 0;
+    nBalanceInQuestion = 0;
+
+    LOCK(cs_wallet);
+    vector<const CWalletTx*> vCoins;
+    vCoins.reserve(mapWallet.size());
+    for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        vCoins.push_back(&(*it).second);
+    CTxDB txdb("r");
+    BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
+    {
+        // Find the corresponding transaction index
+        CTxIndex txindex;
+        if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+            continue;
+        for (int n=0; n < pcoin->vout.size(); n++)
+        {
+            if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+            {
+                printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                nMismatchFound++;
+                nBalanceInQuestion += pcoin->vout[n].nValue;
+            }
+            else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+            {
+                printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                nMismatchFound++;
+                nBalanceInQuestion += pcoin->vout[n].nValue;
+            }
+        }
+    }
+    return (nMismatchFound == 0);
+}
+
+// ppcoin: fix wallet spent state according to txindex
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+    nMismatchFound = 0;
+    nBalanceInQuestion = 0;
+
+    LOCK(cs_wallet);
+    vector<CWalletTx*> vCoins;
+    vCoins.reserve(mapWallet.size());
+    for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+        vCoins.push_back(&(*it).second);
+
+    CTxDB txdb("r");
+    BOOST_FOREACH(CWalletTx* pcoin, vCoins)
+    {
+        // Find the corresponding transaction index
+        CTxIndex txindex;
+        if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+            continue;
+        for (int n=0; n < pcoin->vout.size(); n++)
+        {
+            if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+            {
+                printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                nMismatchFound++;
+                nBalanceInQuestion += pcoin->vout[n].nValue;
+                pcoin->MarkUnspent(n);
+                pcoin->WriteToDisk();
+            }
+            else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+            {
+                printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+                nMismatchFound++;
+                nBalanceInQuestion += pcoin->vout[n].nValue;
+                pcoin->MarkSpent(n);
+                pcoin->WriteToDisk();
+            }
+        }
+    }
+}
+
+// ppcoin: disable transaction (only for coinstake)
+void CWallet::DisableTransaction(const CTransaction &tx)
+{
+    if (!tx.IsCoinStake() || !IsFromMe(tx))
+        return; // only disconnecting coinstake requires marking input unspent
+
+    LOCK(cs_wallet);
+    BOOST_FOREACH(const CTxIn& txin, tx.vin)
+    {
+        map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
+        if (mi != mapWallet.end())
+        {
+            CWalletTx& prev = (*mi).second;
+            if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n]))
+            {
+                prev.MarkUnspent(txin.prevout.n);
+                prev.WriteToDisk();
+            }
+        }
+    }
+}
+
 vector<unsigned char> CReserveKey::GetReservedKey()
 {
     if (nIndex == -1)
index 9e451f8..d3c25f9 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_WALLET_H
@@ -10,6 +11,8 @@
 #include "keystore.h"
 #include "script.h"
 
+extern bool fWalletUnlockStakeOnly;
+
 class CWalletTx;
 class CReserveKey;
 class CWalletDB;
@@ -59,8 +62,8 @@ public:
 class CWallet : public CCryptoKeyStore
 {
 private:
-    bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
-    bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
+    bool SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
+    bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
 
     CWalletDB *pwalletdbEncryption;
 
@@ -145,8 +148,11 @@ public:
     void ResendWalletTransactions();
     int64 GetBalance() const;
     int64 GetUnconfirmedBalance() const;
+    int64 GetStake() const;
+    int64 GetNewMint() const;
     bool CreateTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
     bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
+    bool CreateCoinStake(unsigned int nBits, CTransaction& txNew);
     bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
     std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
     std::string SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
@@ -269,6 +275,10 @@ public:
 
     // get the current wallet format (the oldest client version guaranteed to understand this wallet)
     int GetVersion() { return nWalletVersion; }
+
+    bool CheckSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion);
+    void FixSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion);
+    void DisableTransaction(const CTransaction &tx);
 };
 
 /** A key allocated from the key pool. */
@@ -459,6 +469,18 @@ public:
         }
     }
 
+    void MarkUnspent(unsigned int nOut)
+    {
+        if (nOut >= vout.size())
+            throw std::runtime_error("CWalletTx::MarkUnspent() : nOut out of range");
+        vfSpent.resize(vout.size());
+        if (vfSpent[nOut])
+        {
+            vfSpent[nOut] = false;
+            fAvailableCreditCached = false;
+        }
+    }
+
     bool IsSpent(unsigned int nOut) const
     {
         if (nOut >= vout.size())
@@ -482,7 +504,7 @@ public:
     int64 GetCredit(bool fUseCache=true) const
     {
         // Must wait until coinbase is safely deep enough in the chain before valuing it
-        if (IsCoinBase() && GetBlocksToMaturity() > 0)
+        if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
             return 0;
 
         // GetBalance can assume transactions in mapWallet won't change
@@ -496,7 +518,7 @@ public:
     int64 GetAvailableCredit(bool fUseCache=true) const
     {
         // Must wait until coinbase is safely deep enough in the chain before valuing it
-        if (IsCoinBase() && GetBlocksToMaturity() > 0)
+        if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
             return 0;
 
         if (fUseCache && fAvailableCreditCached)