From: Sunny King Date: Tue, 20 Dec 2011 19:07:42 +0000 (+0000) Subject: Merge bitcoin v0.5.1 into ppcoin X-Git-Tag: v0.4.0-unstable~226 X-Git-Url: https://git.novaco.in/?a=commitdiff_plain;h=de71734ac41eebfe8763ce34e0443d64ea4aacc9;hp=b12fc3e11223557855bb2394e1097afbf0de0b79;p=novacoin.git Merge bitcoin v0.5.1 into ppcoin --- diff --git a/README.ppcoin b/README.ppcoin new file mode 100644 index 0000000..e69de29 diff --git a/bitcointools/.gitignore b/bitcointools/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/bitcointools/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/bitcointools/BCDataStream.py b/bitcointools/BCDataStream.py new file mode 100644 index 0000000..cb2d1d0 --- /dev/null +++ b/bitcointools/BCDataStream.py @@ -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(' : 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 that is public key + value is vector that is private key (full 271-byte + OpenSSL representation) + +wkey : key continues with vector that is public key + value is serialized CWalletKey: + vector that is private key + int64 nTimeCreated + int64 nTimeExpires + string comment + +ckey : enCrypted key continues with vector that is public key + value is vector 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 encrypted key + vector 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 holding other parameters to the derivation algorithm + +defaultkey : + value is vector 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 + +CAddress: + int nVersion + unsigned int nTime + uint64 nServices + unsigned char[12] pchReserved + unsigned int ip + unsigned short port + +CTransaction: + int nVersion + vector vin + vector 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 containing built-in little scripting-language script + (opcodes and arguments to do crypto stuff) + +CMerkleTx: + ... serialize CTransaction, then: + uint256 hashBlock + vector vMerkleBranch + int nIndex + +CWalletTx: + ... serialized CMerkleTx, then: + vector vtxPrev + map mapValue + vector > vOrderForm + unsigned int fTimeReceivedIsTxTime + unsigned int nTimeReceived + char fFromMe + char fSpent + +CInv: + int type : 1:tx 2:block + uint256 hash + +CBlockLocator: + int nVersion + vector # 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: + 1/3/5/9 bytes giving count (see string, above) + followed by that many 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 + (relayed to 10 random nodes so they spread) + +"inv" : + vector + +"getdata" : Asks for blocks or tx's (response is "block" or "tx" messages) + vector + +"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 index 0000000..0939cf3 --- /dev/null +++ b/bitcointools/README.txt @@ -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 index 0000000..e69de29 diff --git a/bitcointools/address.py b/bitcointools/address.py new file mode 100644 index 0000000..8fca690 --- /dev/null +++ b/bitcointools/address.py @@ -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 index 0000000..0d034b8 --- /dev/null +++ b/bitcointools/base58.py @@ -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 index 0000000..faf1113 --- /dev/null +++ b/bitcointools/blkindex.py @@ -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 index 0000000..bff0744 --- /dev/null +++ b/bitcointools/block.py @@ -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 index 0000000..8384751 --- /dev/null +++ b/bitcointools/blocks.py @@ -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 index 0000000..79340da --- /dev/null +++ b/bitcointools/dbdump.py @@ -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 index 0000000..a1764da --- /dev/null +++ b/bitcointools/deserialize.py @@ -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(' 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 index 0000000..d3d2612 --- /dev/null +++ b/bitcointools/enumeration.py @@ -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 index 0000000..dda57a4 --- /dev/null +++ b/bitcointools/fixwallet.py @@ -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 index 0000000..a41ee84 --- /dev/null +++ b/bitcointools/jsonToCSV.py @@ -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 index 0000000..fd2c01f --- /dev/null +++ b/bitcointools/statistics.py @@ -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 index 0000000..5452340 --- /dev/null +++ b/bitcointools/testBCDataStream.py @@ -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 index 0000000..962c8e9 --- /dev/null +++ b/bitcointools/transaction.py @@ -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 index 0000000..4f0adfd --- /dev/null +++ b/bitcointools/util.py @@ -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 index 0000000..a41d3a6 --- /dev/null +++ b/bitcointools/wallet.py @@ -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() diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index bb8d8e2..3ff5c60 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -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 \n" - " is a real and is rounded to the nearest 0.00000001"); + " 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; diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index c7e054d..d20fe24 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -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) { diff --git a/src/init.cpp b/src/init.cpp index dd8bdf5..d36f1dd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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 \t\t " + _("Get help for a command\n") + _("Options:\n") + - " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)\n") + - " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)\n") + + " -conf= \t\t " + _("Specify configuration file (default: ppcoin.conf)\n") + + " -pid= \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="), "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); } diff --git a/src/main.cpp b/src/main.cpp index a7871fc..24a7fd0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 mapNextTx; map 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 diff --git a/src/main.h b/src/main.h index 3870cee..13974f9 100644 --- a/src/main.h +++ b/src/main.h @@ -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) { diff --git a/src/makefile.unix b/src/makefile.unix index 6c48199..b751e3f 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -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 diff --git a/src/net.cpp b/src/net.cpp index e0ac2ab..b968d5a 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -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 index 0000000..3fe6d1a --- /dev/null +++ b/src/ppcoin/genesis.cpp @@ -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((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 index 0000000..d6b7ef3 --- /dev/null +++ b/src/ppcoin/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/util.cpp b/src/util.cpp index 236c7f7..7fb3cd6 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -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& 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(); diff --git a/src/wallet.cpp b/src/wallet.cpp index 28babdb..d605b2c 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -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 >& 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);