Merge bitcoin v0.5.1 into ppcoin
authorSunny King <p2pcoin@gmail.com>
Tue, 20 Dec 2011 19:07:42 +0000 (19:07 +0000)
committerSunny King <p2pcoin@gmail.com>
Tue, 20 Dec 2011 19:12:52 +0000 (19:12 +0000)
33 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/bitcoinrpc.cpp
src/checkpoints.cpp
src/init.cpp
src/main.cpp
src/main.h
src/makefile.unix
src/net.cpp
src/ppcoin/genesis.cpp [new file with mode: 0644]
src/ppcoin/obj/.gitignore [new file with mode: 0644]
src/util.cpp
src/wallet.cpp

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 bb8d8e2..3ff5c60 100644 (file)
@@ -72,7 +72,7 @@ void PrintConsole(const std::string &format, ...)
 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))
@@ -485,12 +485,13 @@ Value settxfee(const Array& params, bool fHelp)
     if (fHelp || params.size() < 1 || params.size() > 1)
         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 the nearest 0.0001\n"
+            "Minimum and default transaction fee is 1 coin");
 
     // Amount
-    int64 nAmount = 0;
-    if (params[0].get_real() != 0.0)
-        nAmount = AmountFromValue(params[0]);        // rejects 0.0 amounts
+    int64 nAmount = MIN_TX_FEE;
+    if (params[0].get_real() != 0.0)                    // rejects 0.0 amounts
+        nAmount = max(nAmount, AmountFromValue(params[0]));
 
     nTransactionFee = nAmount;
     return true;
index c7e054d..d20fe24 100644 (file)
@@ -21,16 +21,8 @@ namespace Checkpoints
     //
     static MapCheckpoints mapCheckpoints =
         boost::assign::map_list_of
-        ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
-        ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
-        ( 68555, uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a"))
-        ( 70567, uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a"))
-        ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
-        (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
-        (118000, uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553"))
-        (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
-        (140700, uint256("0x000000000000033b512028abb90e1626d8b346fd0ed598ac0a3c371138dce2bd"))
-        ;
+        ( 0, hashGenesisBlock )
+        ; // ppcoin: no checkpoint yet; to be created in future releases
 
     bool CheckBlock(int nHeight, const uint256& hash)
     {
index dd8bdf5..d36f1dd 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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"
@@ -91,6 +92,7 @@ void HandleSIGTERM(int)
 // Start
 //
 #if !defined(QT_GUI)
+#if !defined(PPCOIN_GENESIS)
 int main(int argc, char* argv[])
 {
     bool fRet = false;
@@ -102,6 +104,7 @@ int main(int argc, char* argv[])
     return 1;
 }
 #endif
+#endif
 
 bool AppInit(int argc, char* argv[])
 {
@@ -177,8 +180,8 @@ bool AppInit2(int argc, char* argv[])
             "  bitcoind [options] help              \t\t  " + _("List commands\n") +
             "  bitcoind [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") +
@@ -494,7 +497,7 @@ bool AppInit2(int argc, char* argv[])
             wxMessageBox(_("Invalid amount for -paytxfee=<amount>"), "Bitcoin");
             return false;
         }
-        if (nTransactionFee > 0.25 * COIN)
+        if (nTransactionFee >= 10 * COIN)
             wxMessageBox(_("Warning: -paytxfee is set very high.  This is the transaction fee you will pay if you send a transaction."), "Bitcoin", wxOK | wxICON_EXCLAMATION);
     }
 
index a7871fc..24a7fd0 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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"
@@ -28,7 +29,7 @@ unsigned int nTransactionsUpdated = 0;
 map<COutPoint, CInPoint> mapNextTx;
 
 map<uint256, CBlockIndex*> mapBlockIndex;
-uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+uint256 hashGenesisBlock("0x00000000e74ef41733382f8a94d41bf29f20c6c48a7ab489e1fab0ab719bf676");
 static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
 const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
 CBlockIndex* pindexGenesisBlock = NULL;
@@ -53,7 +54,7 @@ int64 nHPSTimerStart;
 
 // Settings
 int fGenerateBitcoins = false;
-int64 nTransactionFee = 0;
+int64 nTransactionFee = MIN_TX_FEE;
 int fLimitProcessors = false;
 int nLimitProcessors = 1;
 int fMinimizeToTray = true;
@@ -427,7 +428,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi
         }
 
         // Don't accept it if it can't get into a block
-        if (nFees < GetMinFee(1000, true, true))
+        if (nFees < GetMinFee(1000, false, true))
             return error("AcceptToMemoryPool() : not enough fees");
 
         // Continuously rate-limit free transactions
@@ -659,7 +660,7 @@ int64 static GetBlockValue(int nHeight, int64 nFees)
     return nSubsidy + nFees;
 }
 
-static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
+static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
 static const int64 nTargetSpacing = 10 * 60;
 static const int64 nInterval = nTargetTimespan / nTargetSpacing;
 
@@ -685,44 +686,22 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
 
 unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast)
 {
-
-    // Genesis block
-    if (pindexLast == NULL)
+    // Genesis block and first block
+    if (pindexLast == NULL || pindexLast->pprev == NULL)
         return bnProofOfWorkLimit.GetCompact();
 
-    // Only change once per interval
-    if ((pindexLast->nHeight+1) % nInterval != 0)
-        return pindexLast->nBits;
-
-    // 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);
-
-    // 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;
-
-    // Retarget
+    int64 nActualSpacing = pindexLast->GetBlockTime() - pindexLast->pprev->GetBlockTime();
+
+    // ppcoin: target change every block
+    // ppcoin: retarget with exponential moving toward target spacing
     CBigNum bnNew;
     bnNew.SetCompact(pindexLast->nBits);
-    bnNew *= nActualTimespan;
-    bnNew /= nTargetTimespan;
+    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();
 }
 
@@ -1526,7 +1505,7 @@ 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.vin.resize(1);
         txNew.vout.resize(1);
@@ -1538,9 +1517,9 @@ bool LoadBlockIndex(bool fAllowNew)
         block.hashPrevBlock = 0;
         block.hashMerkleRoot = block.BuildMerkleTree();
         block.nVersion = 1;
-        block.nTime    = 1231006505;
+        block.nTime    = 1320941849;
         block.nBits    = 0x1d00ffff;
-        block.nNonce   = 2083236893;
+        block.nNonce   = 725069208;
 
         if (fTestNet)
         {
@@ -1553,7 +1532,7 @@ 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("0x09cc647316c90e7e14f225113eec3539e80284695c91e7262a65c72c5d75a868"));
         block.print();
         assert(block.GetHash() == hashGenesisBlock);
 
@@ -2826,9 +2805,8 @@ 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, true);
+            // ppcoin: simplify transaction fee - allow free = false
+            int64 nMinFee = tx.GetMinFee(nBlockSize, false, true);
 
             // Connecting shouldn't fail due to dependency on other memory pool transactions
             // because we're already processing them in order of dependency
index 3870cee..13974f9 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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.
 #ifndef BITCOIN_MAIN_H
@@ -30,11 +31,11 @@ class CBlockIndex;
 static const unsigned int MAX_BLOCK_SIZE = 1000000;
 static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
 static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
-static const int64 COIN = 100000000;
-static const int64 CENT = 1000000;
-static const int64 MIN_TX_FEE = 50000;
+static const int64 COIN = 10000;
+static const int64 CENT = 100;
+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 = 800000000000000 * 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.
@@ -377,7 +378,7 @@ public:
     {
         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
@@ -536,7 +537,7 @@ public:
 
         unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
         unsigned int nNewBlockSize = nBlockSize + nBytes;
-        int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee;
+        int64 nMinFee = nBaseFee;          // ppcoin: simplify transaction fee
 
         if (fAllowFree)
         {
index 6c48199..b751e3f 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.
 
@@ -150,10 +151,19 @@ test_bitcoin: obj/test/test_bitcoin.o $(filter-out obj/nogui/init.o,$(OBJS:obj/%
        $(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-Bstatic -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
 
 clean:
-       -rm -f bitcoind test_bitcoin
+       -rm -f bitcoind test_bitcoin genesis
        -rm -f obj/*.o
        -rm -f obj/nogui/*.o
        -rm -f obj/test/*.o
        -rm -f obj/*.P
        -rm -f obj/nogui/*.P
        -rm -f obj/test/*.P
+       -rm -f ppcoin/obj/*.o
+
+ppcoin/obj/genesis.o: ppcoin/genesis.cpp
+       $(CXX) -c $(CXXFLAGS) -o $@ $<
+       $(CXX) -c $(CXXFLAGS) -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/nogui/init.o
index e0ac2ab..b968d5a 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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.
 
@@ -1223,8 +1224,7 @@ void MapPort(bool /* unused fMapPort */)
 
 
 static const char *strDNSSeed[] = {
-    "bitseed.xf2.org",
-    "dnsseed.bluematt.me",
+    // "seeds.ppcoin.org"
 };
 
 void ThreadDNSAddressSeed(void* parg)
@@ -1292,70 +1292,7 @@ void ThreadDNSAddressSeed2(void* parg)
 
 unsigned int pnSeed[] =
 {
-    0x6884ac63, 0x3ffecead, 0x2919b953, 0x0942fe50, 0x7a1d922e, 0xcdd6734a, 0x953a5bb6, 0x2c46922e,
-    0xe2a5f143, 0xaa39103a, 0xa06afa5c, 0x135ffd59, 0xe8e82863, 0xf61ef029, 0xf75f042e, 0x2b363532,
-    0x29b2df42, 0x16b1f64e, 0xd46e281b, 0x5280bf58, 0x60372229, 0x1be58e4f, 0xa8496f45, 0x1fb1a057,
-    0x756b3844, 0x3bb79445, 0x0b375518, 0xcccb0102, 0xb682bf2e, 0x46431c02, 0x3a81073a, 0xa3771f1f,
-    0x213a121f, 0x85dc2c1b, 0x56b4323b, 0xb34e8945, 0x3c40b33d, 0xfa276418, 0x1f818d29, 0xebe1e344,
-    0xf6160a18, 0xf4fa384a, 0x34b09558, 0xb882b543, 0xe3ce2253, 0x6abf56d8, 0xe91b1155, 0x688ee6ad,
-    0x2efc6058, 0x4792cd47, 0x0c32f757, 0x4c813a46, 0x8c93644a, 0x37507444, 0x813ad218, 0xdac06d4a,
-    0xe4c63e4b, 0x21a1ea3c, 0x8d88556f, 0x30e9173a, 0x041f681b, 0xdc77ba50, 0xc0072753, 0xceddd44f,
-    0x052d1743, 0xe3c77a4a, 0x13981c3a, 0x5685d918, 0x3c0e4e70, 0x3e56fb54, 0xb676ae0c, 0xac93c859,
-    0x22279f43, 0x975a4542, 0xe527f071, 0xea162f2e, 0x3c65a32e, 0x5be5713b, 0x961ec418, 0xb202922e,
-    0x5ef7be50, 0xce49f53e, 0x05803b47, 0x8463b055, 0x78576153, 0x3ec2ae3a, 0x4bbd7118, 0xafcee043,
-    0x56a3e8ba, 0x6174de4d, 0x8d01ba4b, 0xc9af564e, 0xdbc9c547, 0xa627474d, 0xdada9244, 0xd3b3083a,
-    0x523e071f, 0xd6b96f18, 0xbd527c46, 0xdf2bbb4d, 0xd37b4a4b, 0x3a6a2158, 0xc064b055, 0x18a8e055,
-    0xec4dae3b, 0x0540416c, 0x475b4fbe, 0x064803b2, 0x48e9f062, 0x2898524b, 0xd315ff43, 0xf786d247,
-    0xc7ea2f3e, 0xc087f043, 0xc163354b, 0x8250284d, 0xed300029, 0xbf36e05c, 0x8eb3ae4c, 0xe7aa623e,
-    0x7ced0274, 0xdd362c1b, 0x362b995a, 0xca26b629, 0x3fc41618, 0xb97b364e, 0xa05b8729, 0x0f5e3c43,
-    0xdf942618, 0x6aeb9b5b, 0xbf04762e, 0xfaaeb118, 0x87579958, 0x76520044, 0xc2660c5b, 0x628b201b,
-    0xf193932e, 0x1c0ad045, 0xff908346, 0x8da9d4da, 0xed201c1f, 0xa47a2b1b, 0x330007d4, 0x8ba1ed47,
-    0xb2f02d44, 0x7db62c1b, 0x781c454b, 0xc0300029, 0xb7062a45, 0x88b52e3a, 0x78dd6b63, 0x1cb9b718,
-    0x5d358e47, 0x59912c3b, 0x79607544, 0x5197f759, 0xc023be48, 0xd1013743, 0x0f354057, 0x8e3aac3b,
-    0x4114693e, 0x22316318, 0xe27dda50, 0x878eac3b, 0x4948a21f, 0x5db7f24c, 0x8ccb6157, 0x26a5de18,
-    0x0a11bd43, 0x27bb1e41, 0x60a7a951, 0x3e16b35e, 0x07888b53, 0x5648a853, 0x0149fe50, 0xd070a34f,
-    0x6454c96d, 0xd6e54758, 0xa96dc152, 0x65447861, 0xf6bdf95e, 0x10400202, 0x2c29d483, 0x18174732,
-    0x1d840618, 0x12e61818, 0x089d3f3c, 0x917e931f, 0xd1b0c90e, 0x25bd3c42, 0xeb05775b, 0x7d550c59,
-    0x6cfacb01, 0xe4224444, 0xa41dd943, 0x0f5aa643, 0x5e33731b, 0x81036d50, 0x6f46a0d1, 0x7731be43,
-    0x14840e18, 0xf1e8d059, 0x661d2b1f, 0x40a3201b, 0x9407b843, 0xedf0254d, 0x7bd1a5bc, 0x073dbe51,
-    0xe864a97b, 0x2efd947b, 0xb9ca0e45, 0x4e2113ad, 0xcc305731, 0xd39ca63c, 0x733df918, 0xda172b1f,
-    0xaa03b34d, 0x7230fd4d, 0xf1ce6e3a, 0x2e9fab43, 0xa4010750, 0xa928bd18, 0x6809be42, 0xb19de348,
-    0xff956270, 0x0d795f51, 0xd2dec247, 0x6df5774b, 0xbac11f79, 0xdfb05c75, 0x887683d8, 0xa1e83632,
-    0x2c0f7671, 0x28bcb65d, 0xac2a7545, 0x3eebfc60, 0x304ad7c4, 0xa215a462, 0xc86f0f58, 0xcfb92ebe,
-    0x5e23ed82, 0xf506184b, 0xec0f19b7, 0x060c59ad, 0x86ee3174, 0x85380774, 0xa199a562, 0x02b507ae,
-    0x33eb2163, 0xf2112b1f, 0xb702ba50, 0x131b9618, 0x90ccd04a, 0x08f3273b, 0xecb61718, 0x64b8b44d,
-    0x182bf4dc, 0xc7b68286, 0x6e318d5f, 0xfdb03654, 0xb3272e54, 0xe014ad4b, 0x274e4a31, 0x7806375c,
-    0xbc34a748, 0x1b5ad94a, 0x6b54d10e, 0x73e2ae6e, 0x5529d483, 0x8455a76d, 0x99c13f47, 0x1d811741,
-    0xa9782a78, 0x0b00464d, 0x7266ea50, 0x532dab46, 0x33e1413e, 0x780d0c18, 0x0fb0854e, 0x03370155,
-    0x2693042e, 0xfa3d824a, 0x2bb1681b, 0x37ea2a18, 0x7fb8414b, 0x32e0713b, 0xacf38d3f, 0xa282716f,
-    0xb1a09d7b, 0xa04b764b, 0x83c94d18, 0x05ee4c6d, 0x0e795f51, 0x46984352, 0xf80fc247, 0x3fccb946,
-    0xd7ae244b, 0x0a8e0a4c, 0x57b141bc, 0x3647bed1, 0x1431b052, 0x803a8bbb, 0xfc69056b, 0xf5991862,
-    0x14963b2e, 0xd35d5dda, 0xc6c73574, 0xc8f1405b, 0x0ca4224d, 0xecd36071, 0xa9461754, 0xe7a0ed72,
-    0x559e8346, 0x1c9beec1, 0xc786ea4a, 0x9561b44d, 0x9788074d, 0x1a69934f, 0x23c5614c, 0x07c79d4b,
-    0xc7ee52db, 0xc72df351, 0xcb135e44, 0xa0988346, 0xc211fc4c, 0x87dec34b, 0x1381074d, 0x04a65cb7,
-    0x4409083a, 0x4a407a4c, 0x92b8d37d, 0xacf50b4d, 0xa58aa5bc, 0x448f801f, 0x9c83762e, 0x6fd5734a,
-    0xfe2d454b, 0x84144c55, 0x05190e4c, 0xb2151448, 0x63867a3e, 0x16099018, 0x9c010d3c, 0x962d8f3d,
-    0xd51ee453, 0x9d86801f, 0x68e87b47, 0x6bf7bb73, 0x5fc7910e, 0x10d90118, 0x3db04442, 0x729d3e4b,
-    0xc397d842, 0x57bb15ad, 0x72f31f4e, 0xc9380043, 0x2bb24e18, 0xd9b8ab50, 0xb786801f, 0xf4dc4847,
-    0x85f4bb51, 0x4435995b, 0x5ba07e40, 0x2c57392e, 0x3628124b, 0x9839b64b, 0x6fe8b24d, 0xaddce847,
-    0x75260e45, 0x0c572a43, 0xfea21902, 0xb9f9742e, 0x5a70d443, 0x8fc5910e, 0x868d4744, 0x56245e02,
-    0xd7eb5f02, 0x35c12c1b, 0x4373034b, 0x8786554c, 0xa6facf18, 0x4b11a31f, 0x3570664e, 0x5a64bc42,
-    0x0b03983f, 0x8f457e4c, 0x0fd874c3, 0xb6cf31b2, 0x2bbc2d4e, 0x146ca5b2, 0x9d00b150, 0x048a4153,
-    0xca4dcd43, 0xc1607cca, 0x8234cf57, 0x9c7daead, 0x3dc07658, 0xea5c6e4c, 0xf1a0084e, 0x16d2ee53,
-    0x1b849418, 0xfe913a47, 0x1e988f62, 0x208b644c, 0xc55ee980, 0xbdbce747, 0xf59a384e, 0x0f56091b,
-    0x7417b745, 0x0c37344e, 0x2c62ab47, 0xf8533a4d, 0x8030084d, 0x76b93c4b, 0xda6ea0ad, 0x3c54f618,
-    0x63b0de1f, 0x7370d858, 0x1a70bb4c, 0xdda63b2e, 0x60b2ba50, 0x1ba7d048, 0xbe1b2c1b, 0xabea5747,
-    0x29ad2e4d, 0xe8cd7642, 0x66c80e18, 0x138bf34a, 0xc6145e44, 0x2586794c, 0x07bc5478, 0x0da0b14d,
-    0x8f95354e, 0x9eb11c62, 0xa1545e46, 0x2e7a2602, 0x408c9c3d, 0x59065d55, 0xf51d1a4c, 0x3bbc6a4e,
-    0xc71b2a2e, 0xcdaaa545, 0x17d659d0, 0x5202e7ad, 0xf1b68445, 0x93375961, 0xbd88a043, 0x066ad655,
-    0x890f6318, 0x7b7dca47, 0x99bdd662, 0x3bb4fc53, 0x1231efdc, 0xc0a99444, 0x96bbea47, 0x61ed8748,
-    0x27dfa73b, 0x8d4d1754, 0x3460042e, 0x551f0c4c, 0x8d0e0718, 0x162ddc53, 0x53231718, 0x1ecd65d0,
-    0x944d28bc, 0x3b79d058, 0xaff97fbc, 0x4860006c, 0xc101c90e, 0xace41743, 0xa5975d4c, 0x5cc2703e,
-    0xb55a4450, 0x02d18840, 0xee2765ae, 0xd6012fd5, 0x24c94d7d, 0x8c6eec47, 0x7520ba5d, 0x9e15e460,
-    0x8510b04c, 0x75ec3847, 0x1dfa6661, 0xe172b3ad, 0x5744c90e, 0x52a0a152, 0x8d6fad18, 0x67b74b6d,
-    0x93a089b2, 0x0f3ac5d5, 0xe5de1855, 0x43d25747, 0x4bad804a, 0x55b408d8, 0x60a36441, 0xf553e860,
-    0xdb2fa2c8, 0x03152b32, 0xdd27a7d5, 0x3116a8b8, 0x0a1d708c, 0xeee2f13c, 0x6acf436f, 0xce6eb4ca,
-    0x101cd3d9, 0x1c48a6b8, 0xe57d6f44, 0x93dcf562,
+    0xfc01a8c0
 };
 
 
diff --git a/src/ppcoin/genesis.cpp b/src/ppcoin/genesis.cpp
new file mode 100644 (file)
index 0000000..3fe6d1a
--- /dev/null
@@ -0,0 +1,54 @@
+// 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].nValue = 50 * COIN;
+    txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
+    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 % 1048576 == 0)
+            printf("n=%dM hash=%s\n", block.nNonce / 1048576,
+                   block.GetHash().ToString().c_str());
+        block.nTime = GetAdjustedTime();
+        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 236c7f7..7fb3cd6 100644 (file)
@@ -1,5 +1,6 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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"
@@ -336,7 +337,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".%04"PRI64d, quotient, remainder);
 
     // Right-trim excess 0's before the decimal point:
     int nTrim = 0;
@@ -745,12 +746,12 @@ string MyGetSpecialFolderPath(int nFolder, bool fCreate)
 
 string GetDefaultDataDir()
 {
-    // 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
     char* pszHome = getenv("HOME");
     if (pszHome == NULL || strlen(pszHome) == 0)
@@ -762,10 +763,10 @@ string GetDefaultDataDir()
     // Mac
     strHome += "Library/Application Support/";
     filesystem::create_directory(strHome.c_str());
-    return strHome + "Bitcoin";
+    return strHome + "PPCoin";
 #else
     // Unix
-    return strHome + ".bitcoin";
+    return strHome + ".ppcoin";
 #endif
 #endif
 }
@@ -815,7 +816,7 @@ string GetDataDir()
 string GetConfigFile()
 {
     namespace fs = boost::filesystem;
-    fs::path pathConfig(GetArg("-conf", "bitcoin.conf"));
+    fs::path pathConfig(GetArg("-conf", "ppcoin.conf"));
     if (!pathConfig.is_complete())
         pathConfig = fs::path(GetDataDir()) / pathConfig;
     return pathConfig.string();
@@ -847,7 +848,7 @@ void ReadConfigFile(map<string, string>& mapSettingsRet,
 string GetPidFile()
 {
     namespace fs = boost::filesystem;
-    fs::path pathConfig(GetArg("-pid", "bitcoind.pid"));
+    fs::path pathConfig(GetArg("-pid", "ppcoind.pid"));
     if (!pathConfig.is_complete())
         pathConfig = fs::path(GetDataDir()) / pathConfig;
     return pathConfig.string();
index 28babdb..d605b2c 100644 (file)
@@ -1,5 +1,6 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2011 Satoshi Nakamoto
 // Copyright (c) 2011 The Bitcoin developers
+// 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.
 
@@ -1010,9 +1011,8 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
                 dPriority /= nBytes;
 
                 // Check that enough fee is included
-                int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
-                bool fAllowFree = CTransaction::AllowFree(dPriority);
-                int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree);
+                int64 nPayFee = nTransactionFee;   // ppcoin: simplify tx fee
+                int64 nMinFee = wtxNew.GetMinFee(1, false);
                 if (nFeeRet < max(nPayFee, nMinFee))
                 {
                     nFeeRet = max(nPayFee, nMinFee);