--- /dev/null
+#
+# 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)
--- /dev/null
+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.
--- /dev/null
+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) );'
+
+
--- /dev/null
+----- 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.
--- /dev/null
+#
+# 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()
--- /dev/null
+#!/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')
--- /dev/null
+#
+# 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()
--- /dev/null
+#
+# 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'])
--- /dev/null
+#
+# 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()
+
--- /dev/null
+#!/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()
--- /dev/null
+#
+#
+#
+
+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)"
--- /dev/null
+#
+# 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]
--- /dev/null
+#!/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()
--- /dev/null
+#!/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)
+
--- /dev/null
+#!/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()
--- /dev/null
+#
+# 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()
--- /dev/null
+#
+# 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()
+
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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()
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin Developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
};
/** base58-encoded bitcoin addresses.
- * Public-key-hash-addresses have version 0 (or 111 testnet).
+ * Public-key-hash-addresses have version 55 (or 111 testnet).
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
- * Script-hash-addresses have version 5 (or 196 testnet).
+ * Script-hash-addresses have version 57 (or 196 testnet).
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
*/
class CBitcoinAddress : public CBase58Data
public:
enum
{
- PUBKEY_ADDRESS = 0,
- SCRIPT_ADDRESS = 5,
+ PUBKEY_ADDRESS = 55, // ppcoin: addresses begin with 'P'
+ SCRIPT_ADDRESS = 57, // ppcoin: addresses begin with 'Q'
PUBKEY_ADDRESS_TEST = 111,
SCRIPT_ADDRESS_TEST = 196,
};
BN_mpi2bn(pch, p - pch, this);
}
+ uint64 getuint64()
+ {
+ unsigned int nSize = BN_bn2mpi(this, NULL);
+ if (nSize < 4)
+ return 0;
+ std::vector<unsigned char> vch(nSize);
+ BN_bn2mpi(this, &vch[0]);
+ if (vch.size() > 4)
+ vch[4] &= 0x7f;
+ uint64 n = 0;
+ for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
+ ((unsigned char*)&n)[i] = vch[j];
+ return n;
+ }
+
void setuint256(uint256 n)
{
unsigned char pch[sizeof(n) + 6];
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "walletdb.h"
#include "net.h"
#include "init.h"
+#include "checkpoints.h"
#include "ui_interface.h"
#include "bitcoinrpc.h"
if (pindexBest == NULL)
return 1.0;
else
- blockindex = pindexBest;
+ blockindex = GetLastBlockIndex(pindexBest, false);
}
int nShift = (blockindex->nBits >> 24) & 0xff;
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))
if (fHelp || params.size() != 0)
throw runtime_error(
"stop\n"
- "Stop bitcoin server.");
+ "Stop ppcoin server.");
// Shutdown will take long enough that the response should get back
StartShutdown();
- return "bitcoin server stopping";
+ return "ppcoin server stopping";
}
"Returns an object containing various state info.");
Object obj;
- obj.push_back(Pair("version", (int)CLIENT_VERSION));
+ obj.push_back(Pair("version", FormatFullVersion()));
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
+ obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint())));
+ obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake())));
obj.push_back(Pair("blocks", (int)nBestHeight));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string())));
+ obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("testnet", fTestNet));
obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime()));
if (fHelp || params.size() > 1)
throw runtime_error(
"getnewaddress [account]\n"
- "Returns a new bitcoin address for receiving payments. "
+ "Returns a new ppcoin address for receiving payments. "
"If [account] is specified (recommended), it is added to the address book "
"so payments received with the address will be credited to [account].");
if (fHelp || params.size() != 1)
throw runtime_error(
"getaccountaddress <account>\n"
- "Returns the current bitcoin address for receiving payments to this account.");
+ "Returns the current ppcoin address for receiving payments to this account.");
// Parse the account first so we don't generate a key if there's an error
string strAccount = AccountFromValue(params[0]);
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "setaccount <bitcoinaddress> <account>\n"
+ "setaccount <ppcoinaddress> <account>\n"
"Sets the account associated with the given address.");
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
- throw JSONRPCError(-5, "Invalid bitcoin address");
+ throw JSONRPCError(-5, "Invalid ppcoin address");
string strAccount;
{
if (fHelp || params.size() != 1)
throw runtime_error(
- "getaccount <bitcoinaddress>\n"
+ "getaccount <ppcoinaddress>\n"
"Returns the account associated with the given address.");
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
- throw JSONRPCError(-5, "Invalid bitcoin address");
+ throw JSONRPCError(-5, "Invalid ppcoin address");
string strAccount;
map<CBitcoinAddress, string>::iterator mi = pwalletMain->mapAddressBook.find(address);
Value settxfee(const Array& params, bool fHelp)
{
- if (fHelp || params.size() < 1 || params.size() > 1)
+ if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE)
throw runtime_error(
"settxfee <amount>\n"
- "<amount> is a real and is rounded to the nearest 0.00000001");
+ "<amount> is a real and is rounded to 0.01 (cent)\n"
+ "Minimum and default transaction fee per KB is 1 cent");
- // Amount
- int64 nAmount = 0;
- if (params[0].get_real() != 0.0)
- nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts
-
- nTransactionFee = nAmount;
+ nTransactionFee = AmountFromValue(params[0]);
+ nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent
return true;
}
{
if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
throw runtime_error(
- "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
- "<amount> is a real and is rounded to the nearest 0.00000001\n"
+ "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
+ "<amount> is a real and is rounded to the nearest 0.000001\n"
"requires wallet passphrase to be set with walletpassphrase first");
if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4))
throw runtime_error(
- "sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to]\n"
- "<amount> is a real and is rounded to the nearest 0.00000001");
+ "sendtoaddress <ppcoinaddress> <amount> [comment] [comment-to]\n"
+ "<amount> is a real and is rounded to the nearest 0.000001");
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid())
- throw JSONRPCError(-5, "Invalid bitcoin address");
+ throw JSONRPCError(-5, "Invalid ppcoin address");
// Amount
int64 nAmount = AmountFromValue(params[1]);
{
if (fHelp || params.size() != 2)
throw runtime_error(
- "signmessage <bitcoinaddress> <message>\n"
+ "signmessage <ppcoinaddress> <message>\n"
"Sign a message with the private key of an address");
if (pwalletMain->IsLocked())
{
if (fHelp || params.size() != 3)
throw runtime_error(
- "verifymessage <bitcoinaddress> <signature> <message>\n"
+ "verifymessage <ppcoinaddress> <signature> <message>\n"
"Verify a signed message");
string strAddress = params[0].get_str();
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "getreceivedbyaddress <bitcoinaddress> [minconf=1]\n"
- "Returns the total amount received by <bitcoinaddress> in transactions with at least [minconf] confirmations.");
+ "getreceivedbyaddress <ppcoinaddress> [minconf=1]\n"
+ "Returns the total amount received by <ppcoinaddress> in transactions with at least [minconf] confirmations.");
// Bitcoin address
CBitcoinAddress address = CBitcoinAddress(params[0].get_str());
CScript scriptPubKey;
if (!address.IsValid())
- throw JSONRPCError(-5, "Invalid bitcoin address");
+ throw JSONRPCError(-5, "Invalid ppcoin address");
scriptPubKey.SetBitcoinAddress(address);
if (!IsMine(*pwalletMain,scriptPubKey))
return (double)0.0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
throw runtime_error(
- "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
- "<amount> is a real and is rounded to the nearest 0.00000001\n"
+ "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+ "<amount> is a real and is rounded to the nearest 0.000001\n"
"requires wallet passphrase to be set with walletpassphrase first");
if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6))
throw runtime_error(
- "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
- "<amount> is a real and is rounded to the nearest 0.00000001");
+ "sendfrom <fromaccount> <toppcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+ "<amount> is a real and is rounded to the nearest 0.000001");
string strAccount = AccountFromValue(params[0]);
CBitcoinAddress address(params[1].get_str());
if (!address.IsValid())
- throw JSONRPCError(-5, "Invalid bitcoin address");
+ throw JSONRPCError(-5, "Invalid ppcoin address");
int64 nAmount = AmountFromValue(params[2]);
int nMinDepth = 1;
if (params.size() > 3)
{
CBitcoinAddress address(s.name_);
if (!address.IsValid())
- throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_);
+ throw JSONRPCError(-5, string("Invalid ppcoin address:")+s.name_);
if (setAddress.count(address))
throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
if (pwalletMain->IsLocked())
throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.");
+ if (fWalletUnlockStakeOnly)
+ throw JSONRPCError(-13, "Error: Wallet unlocked for coinstake only.");
// Check funds
int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal())
continue;
int nDepth = wtx.GetDepthInMainChain();
Value walletpassphrase(const Array& params, bool fHelp)
{
- if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2))
+ if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3))
throw runtime_error(
- "walletpassphrase <passphrase> <timeout>\n"
- "Stores the wallet decryption key in memory for <timeout> seconds.");
+ "walletpassphrase <passphrase> <timeout> [stakeonly]\n"
+ "Stores the wallet decryption key in memory for <timeout> seconds.\n"
+ "stakeonly is optional true/false allowing only stake creation.");
if (fHelp)
return true;
if (!pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
if (!pwalletMain->IsLocked())
- throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
+ throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings.");
// Note that the walletpassphrase is stored in params[0] which is not mlock()ed
SecureString strWalletPass;
int64* pnSleepTime = new int64(params[1].get_int64());
CreateThread(ThreadCleanWalletPassphrase, pnSleepTime);
+ // ppcoin: if user OS account compromised prevent trivial sendmoney commands
+ if (params.size() > 2)
+ fWalletUnlockStakeOnly = params[2].get_bool();
+ else
+ fWalletUnlockStakeOnly = false;
+
return Value::null;
}
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
StartShutdown();
- return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet";
+ return "wallet encrypted; ppcoin server stopping, restart to run with encrypted wallet";
}
{
if (fHelp || params.size() != 1)
throw runtime_error(
- "validateaddress <bitcoinaddress>\n"
- "Return information about <bitcoinaddress>.");
+ "validateaddress <ppcoinaddress>\n"
+ "Return information about <ppcoinaddress>.");
CBitcoinAddress address(params[0].get_str());
bool isValid = address.IsValid();
"If [data] is specified, tries to solve the block and returns true if it was successful.");
if (vNodes.empty())
- throw JSONRPCError(-9, "Bitcoin is not connected!");
+ throw JSONRPCError(-9, "PPCoin is not connected!");
if (IsInitialBlockDownload())
- throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+ throw JSONRPCError(-10, "PPCoin is downloading blocks...");
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
static mapNewBlock_t mapNewBlock;
nStart = GetTime();
// Create new block
- pblock = CreateNewBlock(reservekey);
+ pblock = CreateNewBlock(pwalletMain, true);
if (!pblock)
throw JSONRPCError(-7, "Out of memory");
vNewBlock.push_back(pblock);
pblock->nNonce = pdata->nNonce;
pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+ if (!pblock->SignBlock(*pwalletMain))
+ throw JSONRPCError(-100, "Unable to sign block");
return CheckWork(pblock, *pwalletMain, reservekey);
}
if (params.size() == 0)
{
if (vNodes.empty())
- throw JSONRPCError(-9, "Bitcoin is not connected!");
+ throw JSONRPCError(-9, "PPCoin is not connected!");
if (IsInitialBlockDownload())
- throw JSONRPCError(-10, "Bitcoin is downloading blocks...");
+ throw JSONRPCError(-10, "PPCoin is downloading blocks...");
static CReserveKey reservekey(pwalletMain);
// Create new block
if(pblock)
delete pblock;
- pblock = CreateNewBlock(reservekey);
+ pblock = CreateNewBlock(pwalletMain);
if (!pblock)
throw JSONRPCError(-7, "Out of memory");
}
Array transactions;
BOOST_FOREACH(CTransaction tx, pblock->vtx) {
- if(tx.IsCoinBase())
+ if(tx.IsCoinBase() || tx.IsCoinStake())
continue;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
}
+// ppcoin: get information of sync-checkpoint
+Value getcheckpoint(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 0)
+ throw runtime_error(
+ "getcheckpoint\n"
+ "Show info of synchronized checkpoint.\n");
+
+ Object result;
+ CBlockIndex* pindexCheckpoint;
+
+ result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
+ pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint];
+ result.push_back(Pair("height", pindexCheckpoint->nHeight));
+ result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", pindexCheckpoint->GetBlockTime()).c_str()));
+
+ return result;
+}
+
+
+// ppcoin: reserve balance from being staked for network protection
+Value reservebalance(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 2)
+ throw runtime_error(
+ "reservebalance [<reserve> [amount]]\n"
+ "<reserve> is true or false to turn balance reserve on or off.\n"
+ "<amount> is a real and rounded to cent.\n"
+ "Set reserve amount not participating in network protection.\n"
+ "If no parameters provided current setting is printed.\n");
+
+ if (params.size() > 0)
+ {
+ bool fReserve = params[0].get_bool();
+ if (fReserve)
+ {
+ if (params.size() == 1)
+ throw runtime_error("must provide amount to reserve balance.\n");
+ int64 nAmount = AmountFromValue(params[1]);
+ nAmount = (nAmount / CENT) * CENT; // round to cent
+ if (nAmount < 0)
+ throw runtime_error("amount cannot be negative.\n");
+ // TODO: handle persistence of nBalanceReserve
+ // settings removed since bitcoin 0.6
+ // WriteSetting("nBalanceReserve", nBalanceReserve = nAmount);
+ nBalanceReserve = nAmount;
+ }
+ else
+ {
+ if (params.size() > 1)
+ throw runtime_error("cannot specify amount to turn off reserve.\n");
+ // TODO: handle persistence of nBalanceReserve
+ // settings removed since bitcoin 0.6
+ // WriteSetting("nBalanceReserve", nBalanceReserve = 0);
+ nBalanceReserve = 0;
+ }
+ }
+
+ Object result;
+ result.push_back(Pair("reserve", (nBalanceReserve > 0)));
+ result.push_back(Pair("amount", ValueFromAmount(nBalanceReserve)));
+ return result;
+}
+
+
+// ppcoin: check wallet integrity
+Value checkwallet(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 0)
+ throw runtime_error(
+ "checkwallet\n"
+ "Check wallet for integrity.\n");
+
+ int nMismatchSpent;
+ int64 nBalanceInQuestion;
+ if (!pwalletMain->CheckSpentCoins(nMismatchSpent, nBalanceInQuestion))
+ {
+ Object result;
+ result.push_back(Pair("mismatched spent coins", nMismatchSpent));
+ result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion)));
+ return result;
+ }
+ return Value::null;
+}
+
+
+// ppcoin: repair wallet
+Value repairwallet(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 0)
+ throw runtime_error(
+ "repairwallet\n"
+ "Repair wallet if checkwallet reports any problem.\n");
+
+ int nMismatchSpent;
+ int64 nBalanceInQuestion;
+ pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion);
+ Object result;
+ if (nMismatchSpent == 0)
+ {
+ result.push_back(Pair("wallet check passed", true));
+ }
+ else
+ {
+ result.push_back(Pair("mismatched spent coins", nMismatchSpent));
+ result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion)));
+ }
+ return result;
+}
+
+// ppcoin: make a public-private key pair
+Value makekeypair(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "makekeypair [prefix]\n"
+ "Make a public/private key pair.\n"
+ "[prefix] is optional preferred prefix for the public key.\n");
+
+ string strPrefix = "";
+ if (params.size() > 0)
+ strPrefix = params[0].get_str();
+
+ CKey key;
+ int nCount = 0;
+ do
+ {
+ key.MakeNewKey(false);
+ nCount++;
+ } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()));
+
+ if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size()))
+ return Value::null;
+
+ CPrivKey vchPrivKey = key.GetPrivKey();
+ Object result;
+ result.push_back(Pair("PrivateKey", HexStr<CPrivKey::iterator>(vchPrivKey.begin(), vchPrivKey.end())));
+ result.push_back(Pair("PublicKey", HexStr(key.GetPubKey())));
+ return result;
+}
+
+extern CCriticalSection cs_mapAlerts;
+extern map<uint256, CAlert> mapAlerts;
+
+// ppcoin: send alert.
+// There is a known deadlock situation with ThreadMessageHandler
+// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages()
+// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage()
+Value sendalert(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 5)
+ throw runtime_error(
+ "sendalert <message> <privatekey> <minver> <maxver> <id> [cancelupto]\n"
+ "<message> is the alert text message\n"
+ "<privatekey> is hex string of alert master private key\n"
+ "<minver> is the minimum applicable client version\n"
+ "<maxver> is the maximum applicable client version\n"
+ "<id> is the alert id\n"
+ "[cancelupto] cancels all alert id's up to this number\n"
+ "Returns true or false.");
+
+ CAlert alert;
+ CKey key;
+
+ alert.strStatusBar = params[0].get_str();
+ alert.nMinVer = params[2].get_int();
+ alert.nMaxVer = params[3].get_int();
+ alert.nID = params[4].get_int();
+ if (params.size() > 5)
+ alert.nCancel = params[5].get_int();
+ alert.nVersion = PROTOCOL_VERSION;
+ alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60;
+ alert.nExpiration = GetAdjustedTime() + 365*24*60*60;
+ alert.nPriority = 1;
+
+ CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+ sMsg << (CUnsignedAlert)alert;
+ alert.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
+
+ vector<unsigned char> vchPrivKey = ParseHex(params[1].get_str());
+ key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+ if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig))
+ throw runtime_error(
+ "Unable to sign alert, check private key?\n");
+ if(!alert.ProcessAlert())
+ throw runtime_error(
+ "Failed to process alert.\n");
+ // Relay alert
+ {
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ alert.RelayTo(pnode);
+ }
+ Object result;
+ result.push_back(Pair("strStatusBar", alert.strStatusBar));
+ result.push_back(Pair("nVersion", alert.nVersion));
+ result.push_back(Pair("nMinVer", alert.nMinVer));
+ result.push_back(Pair("nMaxVer", alert.nMaxVer));
+ result.push_back(Pair("nID", alert.nID));
+ if (alert.nCancel > 0)
+ result.push_back(Pair("nCancel", alert.nCancel));
+ return result;
+}
+
+// ppcoin: send checkpoint
+Value sendcheckpoint(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 2 || params.size() < 1 )
+ throw runtime_error(
+ "sendcheckpoint <privatekey> [checkpointhash]\n"
+ "<privatekey> is hex string of checkpoint master private key\n"
+ "<checkpointhash> is the hash of checkpoint block\n");
+ CSyncCheckpoint checkpoint;
+ CKey key;
+ // TODO: omit checkpointhash parameter
+ if (params.size() > 1)
+ {
+ checkpoint.hashCheckpoint = uint256(params[1].get_str());
+ if (!mapBlockIndex.count(checkpoint.hashCheckpoint))
+ throw runtime_error(
+ "Provided checkpoint block is not on main chain\n");
+ }
+ else
+ {
+ checkpoint.hashCheckpoint = Checkpoints::AutoSelectSyncCheckpoint();
+ if (checkpoint.hashCheckpoint == Checkpoints::hashSyncCheckpoint)
+ throw runtime_error(
+ "Unable to select a more recent sync-checkpoint");
+ }
+ CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION);
+ sMsg << (CUnsignedSyncCheckpoint)checkpoint;
+ checkpoint.vchMsg = vector<unsigned char>(sMsg.begin(), sMsg.end());
+ vector<unsigned char> vchPrivKey = ParseHex(params[0].get_str());
+ key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash
+ if (!key.Sign(Hash(checkpoint.vchMsg.begin(), checkpoint.vchMsg.end()), checkpoint.vchSig))
+ throw runtime_error(
+ "Unable to sign checkpoint, check private key?\n");
+ if(!checkpoint.ProcessSyncCheckpoint(NULL))
+ throw runtime_error(
+ "Failed to process checkpoint.\n");
+ // Relay checkpoint
+ {
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ checkpoint.RelayTo(pnode);
+ }
+ Object result;
+ result.push_back(Pair("checkpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str()));
+ result.push_back(Pair("height", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->nHeight));
+ result.push_back(Pair("timestamp", DateTimeStrFormat("%x %H:%M:%S", mapBlockIndex[Checkpoints::hashSyncCheckpoint]->GetBlockTime()).c_str()));
+ return result;
+}
//
{ "listsinceblock", &listsinceblock, false },
{ "dumpprivkey", &dumpprivkey, false },
{ "importprivkey", &importprivkey, false },
+ { "getcheckpoint", &getcheckpoint, true },
+ { "reservebalance", &reservebalance, false},
+ { "checkwallet", &checkwallet, false},
+ { "repairwallet", &repairwallet, false},
+ { "makekeypair", &makekeypair, false},
+ { "sendalert", &sendalert, false},
+ { "sendcheckpoint", &sendcheckpoint, false},
};
CRPCTable::CRPCTable()
{
ostringstream s;
s << "POST / HTTP/1.1\r\n"
- << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
+ << "User-Agent: ppcoin-json-rpc/" << FormatFullVersion() << "\r\n"
<< "Host: 127.0.0.1\r\n"
<< "Content-Type: application/json\r\n"
<< "Content-Length: " << strMsg.size() << "\r\n"
if (nStatus == 401)
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
"Date: %s\r\n"
- "Server: bitcoin-json-rpc/%s\r\n"
+ "Server: ppcoin-json-rpc/%s\r\n"
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 296\r\n"
"Connection: close\r\n"
"Content-Length: %d\r\n"
"Content-Type: application/json\r\n"
- "Server: bitcoin-json-rpc/%s\r\n"
+ "Server: ppcoin-json-rpc/%s\r\n"
"\r\n"
"%s",
nStatus,
{
unsigned char rand_pwd[32];
RAND_bytes(rand_pwd, 32);
- string strWhatAmI = "To use bitcoind";
+ string strWhatAmI = "To use ppcoind";
if (mapArgs.count("-server"))
strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
else if (mapArgs.count("-daemon"))
asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback();
asio::io_service io_service;
- ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
+ ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT));
ip::tcp::acceptor acceptor(io_service);
try
{
SSLStream sslStream(io_service, context);
SSLIOStreamDevice d(sslStream, fUseSSL);
iostreams::stream<SSLIOStreamDevice> stream(d);
- if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
+ if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str())))
throw runtime_error("couldn't connect to server");
// HTTP basic authentication
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "walletpassphrase" && n > 2) ConvertTo<bool>(params[2]);
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "sendalert" && n > 2) ConvertTo<boost::int64_t>(params[2]);
+ if (strMethod == "sendalert" && n > 3) ConvertTo<boost::int64_t>(params[3]);
+ if (strMethod == "sendalert" && n > 4) ConvertTo<boost::int64_t>(params[4]);
+ if (strMethod == "sendalert" && n > 5) ConvertTo<boost::int64_t>(params[5]);
if (strMethod == "sendmany" && n > 1)
{
string s = params[1].get_str();
params[1] = v.get_obj();
}
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
+ if (strMethod == "reservebalance" && n > 0) ConvertTo<bool>(params[0]);
+ if (strMethod == "reservebalance" && n > 1) ConvertTo<double>(params[1]);
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1)
{
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "checkpoints.h"
+#include "db.h"
#include "main.h"
#include "uint256.h"
namespace Checkpoints
{
- typedef std::map<int, uint256> MapCheckpoints;
+ typedef std::map<int, uint256> MapCheckpoints; // hardened checkpoints
//
// What makes a good checkpoint block?
//
static MapCheckpoints mapCheckpoints =
boost::assign::map_list_of
- ( 11111, uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"))
- ( 33333, uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"))
- ( 74000, uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"))
- (105000, uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"))
- (134444, uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"))
- (168000, uint256("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"))
- (185333, uint256("0x00000000000002334c71b8706940c20348af897a9cfc0f1a6dab0d14d4ceb815"))
- ;
-
- bool CheckBlock(int nHeight, const uint256& hash)
+ ( 0, hashGenesisBlockOfficial )
+ ; // ppcoin: no checkpoint yet; to be created in future releases
+
+ bool CheckHardened(int nHeight, const uint256& hash)
{
if (fTestNet) return true; // Testnet has no checkpoints
CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
{
- if (fTestNet) return NULL;
+ if (fTestNet) {
+ std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hashGenesisBlock);
+ if (t != mapBlockIndex.end())
+ return t->second;
+ return NULL;
+ }
BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
{
}
return NULL;
}
+
+ // ppcoin: synchronized checkpoint (centrally broadcasted)
+ uint256 hashSyncCheckpoint = 0;
+ uint256 hashPendingCheckpoint = 0;
+ CSyncCheckpoint checkpointMessage;
+ CSyncCheckpoint checkpointMessagePending;
+ uint256 hashInvalidCheckpoint = 0;
+ CCriticalSection cs_hashSyncCheckpoint;
+
+ // ppcoin: get last synchronized checkpoint
+ CBlockIndex* GetLastSyncCheckpoint()
+ {
+ LOCK(cs_hashSyncCheckpoint);
+ if (!mapBlockIndex.count(hashSyncCheckpoint))
+ error("GetSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+ else
+ return mapBlockIndex[hashSyncCheckpoint];
+ return NULL;
+ }
+
+ // ppcoin: only descendant of current sync-checkpoint is allowed
+ bool ValidateSyncCheckpoint(uint256 hashCheckpoint)
+ {
+ if (!mapBlockIndex.count(hashSyncCheckpoint))
+ return error("ValidateSyncCheckpoint: block index missing for current sync-checkpoint %s", hashSyncCheckpoint.ToString().c_str());
+ if (!mapBlockIndex.count(hashCheckpoint))
+ return error("ValidateSyncCheckpoint: block index missing for received sync-checkpoint %s", hashCheckpoint.ToString().c_str());
+
+ CBlockIndex* pindexSyncCheckpoint = mapBlockIndex[hashSyncCheckpoint];
+ CBlockIndex* pindexCheckpointRecv = mapBlockIndex[hashCheckpoint];
+
+ if (pindexCheckpointRecv->nHeight <= pindexSyncCheckpoint->nHeight)
+ {
+ // Received an older checkpoint, trace back from current checkpoint
+ // to the same height of the received checkpoint to verify
+ // that current checkpoint should be a descendant block
+ CBlockIndex* pindex = pindexSyncCheckpoint;
+ while (pindex->nHeight > pindexCheckpointRecv->nHeight)
+ if (!(pindex = pindex->pprev))
+ return error("ValidateSyncCheckpoint: pprev1 null - block index structure failure");
+ if (pindex->GetBlockHash() != hashCheckpoint)
+ {
+ hashInvalidCheckpoint = hashCheckpoint;
+ return error("ValidateSyncCheckpoint: new sync-checkpoint %s is conflicting with current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+ }
+ return false; // ignore older checkpoint
+ }
+
+ // Received checkpoint should be a descendant block of the current
+ // checkpoint. Trace back to the same height of current checkpoint
+ // to verify.
+ CBlockIndex* pindex = pindexCheckpointRecv;
+ while (pindex->nHeight > pindexSyncCheckpoint->nHeight)
+ if (!(pindex = pindex->pprev))
+ return error("ValidateSyncCheckpoint: pprev2 null - block index structure failure");
+ if (pindex->GetBlockHash() != hashSyncCheckpoint)
+ {
+ hashInvalidCheckpoint = hashCheckpoint;
+ return error("ValidateSyncCheckpoint: new sync-checkpoint %s is not a descendant of current sync-checkpoint %s", hashCheckpoint.ToString().c_str(), hashSyncCheckpoint.ToString().c_str());
+ }
+ return true;
+ }
+
+ bool WriteSyncCheckpoint(const uint256& hashCheckpoint)
+ {
+ CTxDB txdb;
+ txdb.TxnBegin();
+ if (!txdb.WriteSyncCheckpoint(hashCheckpoint))
+ {
+ txdb.TxnAbort();
+ return error("WriteSyncCheckpoint(): failed to write to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+ }
+ if (!txdb.TxnCommit())
+ return error("WriteSyncCheckpoint(): failed to commit to db sync checkpoint %s", hashCheckpoint.ToString().c_str());
+ txdb.Close();
+
+ Checkpoints::hashSyncCheckpoint = hashCheckpoint;
+ return true;
+ }
+
+ bool AcceptPendingSyncCheckpoint()
+ {
+ LOCK(cs_hashSyncCheckpoint);
+ if (hashPendingCheckpoint != 0 && mapBlockIndex.count(hashPendingCheckpoint))
+ {
+ if (!ValidateSyncCheckpoint(hashPendingCheckpoint))
+ {
+ hashPendingCheckpoint = 0;
+ checkpointMessagePending.SetNull();
+ return false;
+ }
+
+ CTxDB txdb;
+ CBlockIndex* pindexCheckpoint = mapBlockIndex[hashPendingCheckpoint];
+ if (!pindexCheckpoint->IsInMainChain())
+ {
+ txdb.TxnBegin();
+ if (!Reorganize(txdb, pindexCheckpoint))
+ {
+ txdb.TxnAbort();
+ hashInvalidCheckpoint = hashPendingCheckpoint;
+ return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+ }
+ }
+ txdb.Close();
+
+ if (!WriteSyncCheckpoint(hashPendingCheckpoint))
+ return error("AcceptPendingSyncCheckpoint(): failed to write sync checkpoint %s", hashPendingCheckpoint.ToString().c_str());
+ hashPendingCheckpoint = 0;
+ checkpointMessage = checkpointMessagePending;
+ checkpointMessagePending.SetNull();
+ printf("AcceptPendingSyncCheckpoint : sync-checkpoint at %s\n", hashSyncCheckpoint.ToString().c_str());
+ // relay the checkpoint
+ if (!checkpointMessage.IsNull())
+ {
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ checkpointMessage.RelayTo(pnode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ uint256 AutoSelectSyncCheckpoint()
+ {
+ // select a block some time ago
+ CBlockIndex *pindex = mapBlockIndex[hashSyncCheckpoint];
+ while (pindex->pnext && pindex->pnext->GetBlockTime() + CHECKPOINT_MIN_SPAN <= GetAdjustedTime())
+ pindex = pindex->pnext;
+ return pindex->GetBlockHash();
+ }
+
+ // Check against synchronized checkpoint
+ bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
+ {
+ if (fTestNet) return true; // Testnet has no checkpoints
+ int nHeight = pindexPrev->nHeight + 1;
+
+ LOCK(cs_hashSyncCheckpoint);
+ // sync-checkpoint should always be accepted block
+ assert(mapBlockIndex.count(hashSyncCheckpoint));
+ const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
+
+ if (nHeight > pindexSync->nHeight)
+ {
+ // trace back to same height as sync-checkpoint
+ const CBlockIndex* pindex = pindexPrev;
+ while (pindex->nHeight > pindexSync->nHeight)
+ if (!(pindex = pindex->pprev))
+ return error("CheckSync: pprev null - block index structure failure");
+ if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
+ return false; // only descendant of sync-checkpoint can pass check
+ }
+ if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
+ return false; // same height with sync-checkpoint
+ if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
+ return false; // lower height than sync-checkpoint
+ return true;
+ }
+
+ bool WantedByPendingSyncCheckpoint(uint256 hashBlock)
+ {
+ LOCK(cs_hashSyncCheckpoint);
+ if (hashPendingCheckpoint == 0)
+ return false;
+ if (hashBlock == hashPendingCheckpoint)
+ return true;
+ if (mapOrphanBlocks.count(hashPendingCheckpoint)
+ && hashBlock == WantedByOrphan(mapOrphanBlocks[hashPendingCheckpoint]))
+ return true;
+ return false;
+ }
+
+ // ppcoin: reset synchronized checkpoint to last hardened checkpoint
+ bool ResetSyncCheckpoint()
+ {
+ LOCK(cs_hashSyncCheckpoint);
+ const uint256& hash = mapCheckpoints.rbegin()->second;
+ if (mapBlockIndex.count(hash) && !mapBlockIndex[hash]->IsInMainChain())
+ {
+ // checkpoint block accepted but not yet in main chain
+ printf("ResetSyncCheckpoint: Reorganize to hardened checkpoint %s\n", hash.ToString().c_str());
+ CTxDB txdb;
+ txdb.TxnBegin();
+ if (!Reorganize(txdb, mapBlockIndex[hash]))
+ {
+ txdb.TxnAbort();
+ return error("ResetSyncCheckpoint: Reorganize failed for hardened checkpoint %s", hash.ToString().c_str());
+ }
+ txdb.Close();
+ }
+ else if(!mapBlockIndex.count(hash))
+ {
+ // checkpoint block not yet accepted
+ hashPendingCheckpoint = hash;
+ checkpointMessagePending.SetNull();
+ printf("ResetSyncCheckpoint: pending for sync-checkpoint %s\n", hashPendingCheckpoint.ToString().c_str());
+ }
+
+ BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, mapCheckpoints)
+ {
+ const uint256& hash = i.second;
+ if (mapBlockIndex.count(hash) && mapBlockIndex[hash]->IsInMainChain())
+ {
+ if (!WriteSyncCheckpoint(hash))
+ return error("ResetSyncCheckpoint: failed to write sync checkpoint %s", hash.ToString().c_str());
+ printf("ResetSyncCheckpoint: sync-checkpoint reset to %s\n", hashSyncCheckpoint.ToString().c_str());
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void AskForPendingSyncCheckpoint(CNode* pfrom)
+ {
+ LOCK(cs_hashSyncCheckpoint);
+ if (pfrom && hashPendingCheckpoint != 0 && (!mapBlockIndex.count(hashPendingCheckpoint)) && (!mapOrphanBlocks.count(hashPendingCheckpoint)))
+ pfrom->AskFor(CInv(MSG_BLOCK, hashPendingCheckpoint));
+ }
+}
+
+// ppcoin: sync-checkpoint master key
+const std::string CSyncCheckpoint::strMasterPubKey = "0424f20205e5da98ba632bbd278a11a6499585f62bfb2c782377ef59f0251daab8085fc31471bcb8180bc75ed0fa41bb50c7c084511d54015a3a5241d645c7268a";
+
+// ppcoin: verify signature of sync-checkpoint message
+bool CSyncCheckpoint::CheckSignature()
+{
+ CKey key;
+ if (!key.SetPubKey(ParseHex(CSyncCheckpoint::strMasterPubKey)))
+ return error("CSyncCheckpoint::CheckSignature() : SetPubKey failed");
+ if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
+ return error("CSyncCheckpoint::CheckSignature() : verify signature failed");
+
+ // Now unserialize the data
+ CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
+ sMsg >> *(CUnsignedSyncCheckpoint*)this;
+ return true;
+}
+
+// ppcoin: process synchronized checkpoint
+bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom)
+{
+ if (!CheckSignature())
+ return false;
+
+ LOCK(Checkpoints::cs_hashSyncCheckpoint);
+ if (!mapBlockIndex.count(hashCheckpoint))
+ {
+ // We haven't received the checkpoint chain, keep the checkpoint as pending
+ Checkpoints::hashPendingCheckpoint = hashCheckpoint;
+ Checkpoints::checkpointMessagePending = *this;
+ printf("ProcessSyncCheckpoint: pending for sync-checkpoint %s\n", hashCheckpoint.ToString().c_str());
+ // Ask this guy to fill in what we're missing
+ if (pfrom)
+ {
+ pfrom->PushGetBlocks(pindexBest, hashCheckpoint);
+ // ask directly as well in case rejected earlier by duplicate
+ // proof-of-stake because getblocks may not get it this time
+ pfrom->AskFor(CInv(MSG_BLOCK, mapOrphanBlocks.count(hashCheckpoint)? WantedByOrphan(mapOrphanBlocks[hashCheckpoint]) : hashCheckpoint));
+ }
+ return false;
+ }
+
+ if (!Checkpoints::ValidateSyncCheckpoint(hashCheckpoint))
+ return false;
+
+ CTxDB txdb;
+ CBlockIndex* pindexCheckpoint = mapBlockIndex[hashCheckpoint];
+ if (!pindexCheckpoint->IsInMainChain())
+ {
+ // checkpoint chain received but not yet main chain
+ txdb.TxnBegin();
+ if (!Reorganize(txdb, pindexCheckpoint))
+ {
+ txdb.TxnAbort();
+ Checkpoints::hashInvalidCheckpoint = hashCheckpoint;
+ return error("ProcessSyncCheckpoint: Reorganize failed for sync checkpoint %s", hashCheckpoint.ToString().c_str());
+ }
+ }
+ txdb.Close();
+
+ if (!Checkpoints::WriteSyncCheckpoint(hashCheckpoint))
+ return error("ProcessSyncCheckpoint(): failed to write sync checkpoint %s", hashCheckpoint.ToString().c_str());
+ Checkpoints::checkpointMessage = *this;
+ Checkpoints::hashPendingCheckpoint = 0;
+ Checkpoints::checkpointMessagePending.SetNull();
+ printf("ProcessSyncCheckpoint: sync-checkpoint at %s\n", hashCheckpoint.ToString().c_str());
+ return true;
}
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_CHECKPOINT_H
#define BITCOIN_CHECKPOINT_H
#include <map>
+#include "net.h"
+#include "util.h"
+
+#define STAKE_MIN_AGE (60 * 60 * 24) // minimum age for coin age
+#define CHECKPOINT_MIN_SPAN (60 * 60 * 4) // 4 hours checkpoint
class uint256;
class CBlockIndex;
+class CSyncCheckpoint;
/** Block-chain checkpoints are compiled-in sanity checks.
* They are updated every release or three.
namespace Checkpoints
{
// Returns true if block passes checkpoint checks
- bool CheckBlock(int nHeight, const uint256& hash);
+ bool CheckHardened(int nHeight, const uint256& hash);
// Return conservative estimate of total number of blocks, 0 if unknown
int GetTotalBlocksEstimate();
// Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex);
+
+ extern uint256 hashSyncCheckpoint;
+ extern CSyncCheckpoint checkpointMessage;
+ extern uint256 hashInvalidCheckpoint;
+ extern CCriticalSection cs_hashSyncCheckpoint;
+
+ CBlockIndex* GetLastSyncCheckpoint();
+ bool WriteSyncCheckpoint(const uint256& hashCheckpoint);
+ bool AcceptPendingSyncCheckpoint();
+ uint256 AutoSelectSyncCheckpoint();
+ bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev);
+ bool WantedByPendingSyncCheckpoint(uint256 hashBlock);
+ bool ResetSyncCheckpoint();
+ void AskForPendingSyncCheckpoint(CNode* pfrom);
}
+// ppcoin: synchronized checkpoint
+class CUnsignedSyncCheckpoint
+{
+public:
+ int nVersion;
+ uint256 hashCheckpoint; // checkpoint block
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(this->nVersion);
+ nVersion = this->nVersion;
+ READWRITE(hashCheckpoint);
+ )
+
+ void SetNull()
+ {
+ nVersion = 1;
+ hashCheckpoint = 0;
+ }
+
+ std::string ToString() const
+ {
+ return strprintf(
+ "CSyncCheckpoint(\n"
+ " nVersion = %d\n"
+ " hashCheckpoint = %s\n"
+ ")\n",
+ nVersion,
+ hashCheckpoint.ToString().c_str());
+ }
+
+ void print() const
+ {
+ printf("%s", ToString().c_str());
+ }
+};
+
+class CSyncCheckpoint : public CUnsignedSyncCheckpoint
+{
+public:
+ static const std::string strMasterPubKey;
+
+ std::vector<unsigned char> vchMsg;
+ std::vector<unsigned char> vchSig;
+
+ CSyncCheckpoint()
+ {
+ SetNull();
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(vchMsg);
+ READWRITE(vchSig);
+ )
+
+ void SetNull()
+ {
+ CUnsignedSyncCheckpoint::SetNull();
+ vchMsg.clear();
+ vchSig.clear();
+ }
+
+ bool IsNull() const
+ {
+ return (hashCheckpoint == 0);
+ }
+
+ uint256 GetHash() const
+ {
+ return SerializeHash(*this);
+ }
+
+ bool RelayTo(CNode* pnode) const
+ {
+ // returns true if wasn't already sent
+ if (pnode->hashCheckpointKnown != hashCheckpoint)
+ {
+ pnode->hashCheckpointKnown = hashCheckpoint;
+ pnode->PushMessage("checkpoint", *this);
+ return true;
+ }
+ return false;
+ }
+
+ bool CheckSignature();
+ bool ProcessSyncCheckpoint(CNode* pfrom);
+};
+
#endif
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "db.h"
+#include "net.h"
+#include "checkpoints.h"
#include "util.h"
#include "main.h"
#include <boost/version.hpp>
return Write(string("hashBestChain"), hashBestChain);
}
-bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
+bool CTxDB::ReadBestInvalidTrust(uint64& nBestInvalidTrust)
{
- return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
+ return Read(string("nBestInvalidTrust"), nBestInvalidTrust);
}
-bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
+bool CTxDB::WriteBestInvalidTrust(uint64 nBestInvalidTrust)
{
- return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
+ return Write(string("nBestInvalidTrust"), nBestInvalidTrust);
+}
+
+bool CTxDB::ReadSyncCheckpoint(uint256& hashCheckpoint)
+{
+ return Read(string("hashSyncCheckpoint"), hashCheckpoint);
+}
+
+bool CTxDB::WriteSyncCheckpoint(uint256 hashCheckpoint)
+{
+ return Write(string("hashSyncCheckpoint"), hashCheckpoint);
+}
+
+bool CTxDB::ReadCheckpointPubKey(string& strPubKey)
+{
+ return Read(string("strCheckpointPubKey"), strPubKey);
+}
+
+bool CTxDB::WriteCheckpointPubKey(const string& strPubKey)
+{
+ return Write(string("strCheckpointPubKey"), strPubKey);
}
CBlockIndex static * InsertBlockIndex(uint256 hash)
pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
pindexNew->nFile = diskindex.nFile;
pindexNew->nBlockPos = diskindex.nBlockPos;
+ pindexNew->nChainTrust = diskindex.nChainTrust;
pindexNew->nHeight = diskindex.nHeight;
+ pindexNew->fProofOfStake = diskindex.fProofOfStake;
+ pindexNew->prevoutStake = diskindex.prevoutStake;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
if (!pindexNew->CheckIndex())
return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
+
+ // ppcoin: build setStakeSeen
+ if (pindexNew->fProofOfStake)
+ setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
}
else
{
if (fRequestShutdown)
return true;
- // Calculate bnChainWork
- vector<pair<int, CBlockIndex*> > vSortedByHeight;
- vSortedByHeight.reserve(mapBlockIndex.size());
- BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
- {
- CBlockIndex* pindex = item.second;
- vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
- }
- sort(vSortedByHeight.begin(), vSortedByHeight.end());
- BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
- {
- CBlockIndex* pindex = item.second;
- pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
- }
-
// Load hashBestChain pointer to end of best chain
if (!ReadHashBestChain(hashBestChain))
{
return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
pindexBest = mapBlockIndex[hashBestChain];
nBestHeight = pindexBest->nHeight;
- bnBestChainWork = pindexBest->bnChainWork;
- printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
+ nBestChainTrust = pindexBest->nChainTrust;
+ printf("LoadBlockIndex(): hashBestChain=%s height=%d trust=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, nBestChainTrust);
+
+ // ppcoin: load hashSyncCheckpoint
+ if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
+ return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded");
+ printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
- // Load bnBestInvalidWork, OK if it doesn't exist
- ReadBestInvalidWork(bnBestInvalidWork);
+ // Load nBestInvalidTrust, OK if it doesn't exist
+ ReadBestInvalidTrust(nBestInvalidTrust);
// Verify blocks in the best chain
int nCheckLevel = GetArg("-checklevel", 1);
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_DB_H
bool EraseBlockIndex(uint256 hash);
bool ReadHashBestChain(uint256& hashBestChain);
bool WriteHashBestChain(uint256 hashBestChain);
- bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
- bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
+ bool ReadBestInvalidTrust(uint64& nBestInvalidTrust);
+ bool WriteBestInvalidTrust(uint64 nBestInvalidTrust);
+ bool ReadSyncCheckpoint(uint256& hashCheckpoint);
+ bool WriteSyncCheckpoint(uint256 hashCheckpoint);
+ bool ReadCheckpointPubKey(std::string& strPubKey);
+ bool WriteCheckpointPubKey(const std::string& strPubKey);
bool LoadBlockIndex();
};
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "db.h"
delete pwalletMain;
CreateThread(ExitTimeout, NULL);
Sleep(50);
- printf("Bitcoin exiting\n\n");
+ printf("PPCoin exiting\n\n");
fExit = true;
#ifndef QT_GUI
// ensure non UI client get's exited here, but let Bitcoin-Qt reach return 0; in bitcoin.cpp
// Start
//
#if !defined(QT_GUI)
+#if !defined(PPCOIN_GENESIS)
int main(int argc, char* argv[])
{
bool fRet = false;
return 1;
}
#endif
+#endif
bool AppInit(int argc, char* argv[])
{
if (mapArgs.count("-?") || mapArgs.count("--help"))
{
string strUsage = string() +
- _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" +
+ _("PPCoin version") + " " + FormatFullVersion() + "\n\n" +
_("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" +
- " bitcoind [options] \t " + "\n" +
- " bitcoind [options] <command> [params]\t " + _("Send command to -server or bitcoind") + "\n" +
- " bitcoind [options] help \t\t " + _("List commands") + "\n" +
- " bitcoind [options] help <command> \t\t " + _("Get help for a command") + "\n" +
+ " ppcoind [options] \t " + "\n" +
+ " ppcoind [options] <command> [params]\t " + _("Send command to -server or ppcoind") + "\n" +
+ " ppcoind [options] help \t\t " + _("List commands") + "\n" +
+ " ppcoind [options] help <command> \t\t " + _("Get help for a command") + "\n" +
_("Options:") + "\n" +
- " -conf=<file> \t\t " + _("Specify configuration file (default: bitcoin.conf)") + "\n" +
- " -pid=<file> \t\t " + _("Specify pid file (default: bitcoind.pid)") + "\n" +
+ " -conf=<file> \t\t " + _("Specify configuration file (default: ppcoin.conf)") + "\n" +
+ " -pid=<file> \t\t " + _("Specify pid file (default: ppcoind.pid)") + "\n" +
" -gen \t\t " + _("Generate coins") + "\n" +
" -gen=0 \t\t " + _("Don't generate coins") + "\n" +
" -min \t\t " + _("Start minimized") + "\n" +
" -timeout=<n> \t " + _("Specify connection timeout (in milliseconds)") + "\n" +
" -proxy=<ip:port> \t " + _("Connect through socks4 proxy") + "\n" +
" -dns \t " + _("Allow DNS lookups for addnode and connect") + "\n" +
- " -port=<port> \t\t " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n" +
+ " -port=<port> \t\t " + _("Listen for connections on <port> (default: 9901 or testnet: 9903)") + "\n" +
" -maxconnections=<n>\t " + _("Maintain at most <n> connections to peers (default: 125)") + "\n" +
" -addnode=<ip> \t " + _("Add a node to connect to and attempt to keep the connection open") + "\n" +
" -connect=<ip> \t\t " + _("Connect only to the specified node") + "\n" +
- " -irc \t " + _("Find peers using internet relay chat (default: 0)") + "\n" +
" -listen \t " + _("Accept connections from outside (default: 1)") + "\n" +
#ifdef QT_GUI
" -lang=<lang> \t\t " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
#endif
" -rpcuser=<user> \t " + _("Username for JSON-RPC connections") + "\n" +
" -rpcpassword=<pw>\t " + _("Password for JSON-RPC connections") + "\n" +
- " -rpcport=<port> \t\t " + _("Listen for JSON-RPC connections on <port> (default: 8332)") + "\n" +
+ " -rpcport=<port> \t\t " + _("Listen for JSON-RPC connections on <port> (default: 9902)") + "\n" +
" -rpcallowip=<ip> \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
" -rpcconnect=<ip> \t " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
if (!fDebug)
ShrinkDebugFile();
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
- printf("Bitcoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
+ printf("PPCoin version %s (%s)\n", FormatFullVersion().c_str(), CLIENT_DATE.c_str());
printf("Default data directory %s\n", GetDefaultDataDir().string().c_str());
if (GetBoolArg("-loadblockindextest"))
static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
if (!lock.try_lock())
{
- ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str()), _("Bitcoin"), wxOK|wxMODAL);
+ ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. PPCoin is probably already running."), GetDataDir().string().c_str()), _("PPCoin"), wxOK|wxMODAL);
return false;
}
// Load data files
//
if (fDaemon)
- fprintf(stdout, "bitcoin server starting\n");
+ fprintf(stdout, "ppcoin server starting\n");
int64 nStart;
InitMessage(_("Loading addresses..."));
if (nLoadWalletRet == DB_CORRUPT)
strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n";
else if (nLoadWalletRet == DB_TOO_NEW)
- strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n";
+ strErrors << _("Error loading wallet.dat: Wallet requires newer version of PPCoin") << "\n";
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
- strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n";
+ strErrors << _("Wallet needed to be rewritten: restart PPCoin to complete") << "\n";
printf("%s", strErrors.str().c_str());
- ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL);
+ ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL);
return false;
}
else
if (!strErrors.str().empty())
{
- ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL);
+ ThreadSafeMessageBox(strErrors.str(), _("PPCoin"), wxOK | wxICON_ERROR | wxMODAL);
return false;
}
addrProxy = CService(mapArgs["-proxy"], 9050);
if (!addrProxy.IsValid())
{
- ThreadSafeMessageBox(_("Invalid -proxy address"), _("Bitcoin"), wxOK | wxMODAL);
+ ThreadSafeMessageBox(_("Invalid -proxy address"), _("PPCcoin"), wxOK | wxMODAL);
return false;
}
}
std::string strError;
if (!BindListenPort(strError))
{
- ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL);
+ ThreadSafeMessageBox(strError, _("PPCoin"), wxOK | wxMODAL);
return false;
}
}
if (mapArgs.count("-paytxfee"))
{
- if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee))
+ if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee) || nTransactionFee < MIN_TX_FEE)
{
- ThreadSafeMessageBox(_("Invalid amount for -paytxfee=<amount>"), _("Bitcoin"), wxOK | wxMODAL);
+ ThreadSafeMessageBox(_("Invalid amount for -paytxfee=<amount>"), _("PPCoin"), wxOK | wxMODAL);
return false;
}
if (nTransactionFee > 0.25 * COIN)
- ThreadSafeMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
+ ThreadSafeMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), _("PPCoin"), wxOK | wxICON_EXCLAMATION | wxMODAL);
}
//
RandAddSeedPerfmon();
if (!CreateThread(StartNode, NULL))
- ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("Bitcoin"), wxOK | wxMODAL);
+ ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("PPCoin"), wxOK | wxMODAL);
if (fServer)
CreateThread(ThreadRPCServer, NULL);
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
unsigned int nTransactionsUpdated = 0;
map<uint256, CBlockIndex*> mapBlockIndex;
-uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+set<pair<COutPoint, unsigned int> > setStakeSeen;
+uint256 hashGenesisBlock = hashGenesisBlockOfficial;
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
-CBigNum bnBestChainWork = 0;
-CBigNum bnBestInvalidWork = 0;
+uint64 nBestChainTrust = 0;
+uint64 nBestInvalidTrust = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
+set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
map<uint256, CDataStream*> mapOrphanTransactions;
map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
// Settings
int64 nTransactionFee = 0;
+int64 nBalanceReserve = 0;
}
// make sure all wallets know about the given transaction, in the given block
-void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false)
+void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false, bool fConnect = true)
{
+ if (!fConnect)
+ {
+ // ppcoin: wallets need to refund inputs when disconnecting coinstake
+ if (tx.IsCoinStake())
+ {
+ BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
+ if (pwallet->IsFromMe(tx))
+ pwallet->DisableTransaction(tx);
+ }
+ return;
+ }
+
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
// Check for negative or overflow output values
int64 nValueOut = 0;
- BOOST_FOREACH(const CTxOut& txout, vout)
+ for (int i = 0; i < vout.size(); i++)
{
+ const CTxOut& txout = vout[i];
+ if (txout.IsEmpty() && (!IsCoinBase()) && (!IsCoinStake()))
+ return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
if (txout.nValue < 0)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
if (txout.nValue > MAX_MONEY)
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
+ // ppcoin: coinstake is also only valid in a block, not as a loose transaction
+ if (tx.IsCoinStake())
+ return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx"));
// To help v0.1.5 clients who would see it as a negative number
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
- if (nFees < tx.GetMinFee(1000, true, GMF_RELAY))
+ if (nFees < tx.GetMinFee(1000, false, GMF_RELAY))
return error("CTxMemPool::accept() : not enough fees");
// Continuously rate-limit free transactions
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
int CMerkleTx::GetBlocksToMaturity() const
{
- if (!IsCoinBase())
+ if (!(IsCoinBase() || IsCoinStake()))
return 0;
return max(0, (COINBASE_MATURITY+20) - GetDepthInMainChain());
}
// Add previous supporting transactions first
BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
{
- if (!tx.IsCoinBase())
+ if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();
if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
return pblock->GetHash();
}
-int64 static GetBlockValue(int nHeight, int64 nFees)
+// ppcoin: find block wanted by given orphan block
+uint256 WantedByOrphan(const CBlock* pblockOrphan)
+{
+ // Work back to the first block in the orphan chain
+ while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock))
+ pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock];
+ return pblockOrphan->hashPrevBlock;
+}
+
+int64 static GetProofOfWorkReward(unsigned int nBits)
{
- int64 nSubsidy = 50 * COIN;
+ CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
+ CBigNum bnTarget;
+ bnTarget.SetCompact(nBits);
+ CBigNum bnTargetLimit = bnProofOfWorkLimit;
+ bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
+
+ // ppcoin: subsidy is cut in half every 16x multiply of difficulty
+ // A reasonably continuous curve is used to avoid shock to market
+ // (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
+ CBigNum bnLowerBound = CENT;
+ CBigNum bnUpperBound = bnSubsidyLimit;
+ while (bnLowerBound + CENT <= bnUpperBound)
+ {
+ CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
+ if (fDebug && GetBoolArg("-printcreation"))
+ printf("GetProofOfWorkReward() : lower=%"PRI64d" upper=%"PRI64d" mid=%"PRI64d"\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
+ if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
+ bnUpperBound = bnMidValue;
+ else
+ bnLowerBound = bnMidValue;
+ }
+
+ int64 nSubsidy = bnUpperBound.getuint64();
+ nSubsidy = (nSubsidy / CENT) * CENT;
+ if (fDebug && GetBoolArg("-printcreation"))
+ printf("GetProofOfWorkReward() : create=%s nBits=0x%08x nSubsidy=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nBits, nSubsidy);
- // Subsidy is cut in half every 4 years
- nSubsidy >>= (nHeight / 210000);
+ return nSubsidy;
+}
- return nSubsidy + nFees;
+// ppcoin: miner's coin stake is rewarded based on coin age spent (coin-days)
+int64 GetProofOfStakeReward(int64 nCoinAge)
+{
+ static int64 nRewardCoinYear = CENT; // creation amount per coin-year
+ int64 nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
+ if (fDebug && GetBoolArg("-printcreation"))
+ printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d"\n", FormatMoney(nSubsidy).c_str(), nCoinAge);
+ return nSubsidy;
}
-static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
-static const int64 nTargetSpacing = 10 * 60;
-static const int64 nInterval = nTargetTimespan / nTargetSpacing;
+static const int64 nTargetTimespan = 7 * 24 * 60 * 60; // one week
+static const int64 nTargetSpacingStake = 10 * 60; // ten minutes
+static const int64 nTargetSpacingWorkMax = 2 * 60 * 60; // two hours
+static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours
//
// minimum amount of work that could possibly be required nTime after
//
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
{
- // Testnet has min-difficulty blocks
- // after nTargetSpacing*2 time between blocks:
- if (fTestNet && nTime > nTargetSpacing*2)
- return bnProofOfWorkLimit.GetCompact();
-
CBigNum bnResult;
bnResult.SetCompact(nBase);
+ bnResult *= 2;
while (nTime > 0 && bnResult < bnProofOfWorkLimit)
{
- // Maximum 400% adjustment...
- bnResult *= 4;
- // ... in best-case exactly 4-times-normal target time
- nTime -= nTargetTimespan*4;
+ // Maximum 200% adjustment per day...
+ bnResult *= 2;
+ nTime -= 24 * 60 * 60;
}
if (bnResult > bnProofOfWorkLimit)
bnResult = bnProofOfWorkLimit;
return bnResult.GetCompact();
}
-unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlock *pblock)
+// ppcoin: find last block index up to pindex
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
{
- unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact();
-
- // Genesis block
- if (pindexLast == NULL)
- return nProofOfWorkLimit;
-
- // Only change once per interval
- if ((pindexLast->nHeight+1) % nInterval != 0)
- {
- // Special rules for testnet after 15 Feb 2012:
- if (fTestNet && pblock->nTime > 1329264000)
- {
- // If the new block's timestamp is more than 2* 10 minutes
- // then allow mining of a min-difficulty block.
- if (pblock->nTime - pindexLast->nTime > nTargetSpacing*2)
- return nProofOfWorkLimit;
- else
- {
- // Return the last non-special-min-difficulty-rules-block
- const CBlockIndex* pindex = pindexLast;
- while (pindex->pprev && pindex->nHeight % nInterval != 0 && pindex->nBits == nProofOfWorkLimit)
- pindex = pindex->pprev;
- return pindex->nBits;
- }
- }
-
- return pindexLast->nBits;
- }
+ while (pindex && (pindex->IsProofOfStake() != fProofOfStake))
+ pindex = pindex->pprev;
+ return pindex;
+}
- // Go back by what we want to be 14 days worth of blocks
- const CBlockIndex* pindexFirst = pindexLast;
- for (int i = 0; pindexFirst && i < nInterval-1; i++)
- pindexFirst = pindexFirst->pprev;
- assert(pindexFirst);
+unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake)
+{
+ // Genesis block and first block
+ if (pindexLast == NULL || pindexLast->pprev == NULL)
+ return bnProofOfWorkLimit.GetCompact();
- // Limit adjustment step
- int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
- printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan);
- if (nActualTimespan < nTargetTimespan/4)
- nActualTimespan = nTargetTimespan/4;
- if (nActualTimespan > nTargetTimespan*4)
- nActualTimespan = nTargetTimespan*4;
+ const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
+ if (pindexPrev == NULL)
+ return bnProofOfWorkLimit.GetCompact();
+ const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
+ if (pindexPrevPrev == NULL)
+ return bnProofOfWorkLimit.GetCompact();
+ int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
- // Retarget
+ // ppcoin: target change every block
+ // ppcoin: retarget with exponential moving toward target spacing
CBigNum bnNew;
- bnNew.SetCompact(pindexLast->nBits);
- bnNew *= nActualTimespan;
- bnNew /= nTargetTimespan;
+ bnNew.SetCompact(pindexPrev->nBits);
+ int64 nTargetSpacing = fProofOfStake? nTargetSpacingStake : min(nTargetSpacingWorkMax, nTargetSpacingStake * (1 + pindexLast->nHeight - pindexPrev->nHeight));
+ int64 nInterval = nTargetTimespan / nTargetSpacing;
+ bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
+ bnNew /= ((nInterval + 1) * nTargetSpacing);
if (bnNew > bnProofOfWorkLimit)
bnNew = bnProofOfWorkLimit;
- /// debug print
- printf("GetNextWorkRequired RETARGET\n");
- printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan);
- printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
- printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
-
return bnNew.GetCompact();
}
void static InvalidChainFound(CBlockIndex* pindexNew)
{
- if (pindexNew->bnChainWork > bnBestInvalidWork)
+ if (pindexNew->nChainTrust > nBestInvalidTrust)
{
- bnBestInvalidWork = pindexNew->bnChainWork;
- CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
+ nBestInvalidTrust = pindexNew->nChainTrust;
+ CTxDB().WriteBestInvalidTrust(nBestInvalidTrust);
MainFrameRepaint();
}
- printf("InvalidChainFound: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
- printf("InvalidChainFound: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
- if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6)
+ printf("InvalidChainFound: invalid block=%s height=%d trust=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString().c_str());
+ printf("InvalidChainFound: current best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
+ if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
printf("InvalidChainFound: WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n");
}
void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
{
- nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
-
- // Updating time can change work required on testnet:
- if (fTestNet)
- nBits = GetNextWorkRequired(pindexPrev, this);
+ nTime = max(GetBlockTime(), GetAdjustedTime());
}
return nSigOps;
}
-bool CTransaction::ConnectInputs(MapPrevTx inputs,
+bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
{
if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
- // If prev is coinbase, check that it's matured
- if (txPrev.IsCoinBase())
+ // If prev is coinbase/coinstake, check that it's matured
+ if (txPrev.IsCoinBase() || txPrev.IsCoinStake())
for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
- return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
+ return error("ConnectInputs() : tried to spend coinbase/coinstake at depth %d", pindexBlock->nHeight - pindex->nHeight);
+
+ // ppcoin: check transaction timestamp
+ if (txPrev.nTime > nTime)
+ return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction"));
// Check for negative or overflow input values
nValueIn += txPrev.vout[prevout.n].nValue;
}
}
- if (nValueIn < GetValueOut())
- return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
+ if (IsCoinStake())
+ {
+ // ppcoin: coin stake tx earns reward instead of paying fee
+ uint64 nCoinAge;
+ if (!GetCoinAge(txdb, nCoinAge))
+ return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
+ int64 nStakeReward = GetValueOut() - nValueIn;
+ if (nStakeReward > GetProofOfStakeReward(nCoinAge))
+ return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str()));
+ }
+ else
+ {
+ if (nValueIn < GetValueOut())
+ return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
- // Tally transaction fees
- int64 nTxFee = nValueIn - GetValueOut();
- if (nTxFee < 0)
- return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
- nFees += nTxFee;
- if (!MoneyRange(nFees))
- return DoS(100, error("ConnectInputs() : nFees out of range"));
+ // Tally transaction fees
+ int64 nTxFee = nValueIn - GetValueOut();
+ if (nTxFee < 0)
+ return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
+ // ppcoin: enforce transaction fees for every block
+ if (nTxFee < GetMinFee())
+ return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false;
+ nFees += nTxFee;
+ if (!MoneyRange(nFees))
+ return DoS(100, error("ConnectInputs() : nFees out of range"));
+ }
}
return true;
return error("DisconnectBlock() : WriteBlockIndex failed");
}
+ // ppcoin: clean up wallet after disconnecting coinstake
+ BOOST_FOREACH(CTransaction& tx, vtx)
+ SyncWithWallets(tx, this, false, false);
+
return true;
}
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
//// issue here: it doesn't know the version
- unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
+ unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size());
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
- if (!tx.IsCoinBase())
+ if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
bool fInvalid;
if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
- if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
return false;
}
return error("ConnectBlock() : UpdateTxIndex failed");
}
- if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
+ // ppcoin: fees are not collected by miners as in bitcoin
+ // ppcoin: fees are destroyed to compensate the entire network
+ if (IsProofOfWork() && vtx[0].GetValueOut() > GetProofOfWorkReward(nBits))
return false;
+ if (fDebug && GetBoolArg("-printcreation"))
+ printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
// Update block index on disk without changing it in memory.
// The memory index structure will be changed after the db commits.
return true;
}
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
printf("REORGANIZE\n");
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
- if (!tx.IsCoinBase())
+ if (!(tx.IsCoinBase() || tx.IsCoinStake()))
vResurrect.push_back(tx);
}
// Reorganize is costly in terms of db load, as it works in a single db transaction.
// Try to limit how much needs to be done inside
- while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
+ while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust)
{
vpindexSecondary.push_back(pindexIntermediate);
pindexIntermediate = pindexIntermediate->pprev;
hashBestChain = hash;
pindexBest = pindexNew;
nBestHeight = pindexBest->nHeight;
- bnBestChainWork = pindexNew->bnChainWork;
+ nBestChainTrust = pindexNew->nChainTrust;
nTimeBestReceived = GetTime();
nTransactionsUpdated++;
- printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
+ printf("SetBestChain: new best=%s height=%d trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
std::string strCmd = GetArg("-blocknotify", "");
}
+// ppcoin: coinstake must meet hash target according to the protocol:
+// input 0 must meet the formula
+// hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
+// this ensures that the chance of getting a coinstake is proportional to the
+// amount of coin age one owns.
+// The reason this hash is chosen is the following:
+// nBits: encodes all past block timestamps, making computing hash in advance
+// more difficult
+// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
+// generate transaction for future advantage
+// txPrev.offset: offset of txPrev inside block, to reduce the chance of
+// nodes generating coinstake at the same time
+// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
+// time
+// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
+// generating coinstake at the same time
+// block/tx hash should not be used here as they can be generated in vast
+// quantities so as to generate blocks faster, degrading the system back into
+// a proof-of-work situation.
+//
+bool CTransaction::CheckProofOfStake(unsigned int nBits) const
+{
+ CBigNum bnTargetPerCoinDay;
+ bnTargetPerCoinDay.SetCompact(nBits);
+
+ if (!IsCoinStake())
+ return true;
+
+ // Input 0 must match the stake hash target per coin age (nBits)
+ const CTxIn& txin = vin[0];
+
+ // First try finding the previous transaction in database
+ CTxDB txdb("r");
+ CTransaction txPrev;
+ CTxIndex txindex;
+ if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ return false; // previous transaction not in main chain
+ txdb.Close();
+ if (nTime < txPrev.nTime)
+ return false; // Transaction timestamp violation
+
+ // Verify signature
+ if (!VerifySignature(txPrev, *this, 0, true, 0))
+ return DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", GetHash().ToString().c_str()));
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ return false; // unable to read block of previous transaction
+ if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+ return false; // only count coins meeting min age requirement
+
+ int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, 0);
+ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ return true;
+ else
+ return DoS(100, error("CheckProofOfStake() : check target failed on coinstake %s", GetHash().ToString().c_str()));
+}
+
+// ppcoin: total coin age spent in transaction, in the unit of coin-days.
+// Only those coins meeting minimum age requirement counts. As those
+// transactions not in main chain are not currently indexed so we
+// might not find out about their coin age. Older transactions are
+// guaranteed to be in main chain by sync-checkpoint. This rule is
+// introduced to help nodes establish a consistent view of the coin
+// age (trust score) of competing branches.
+bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const
+{
+ CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds
+ nCoinAge = 0;
+
+ if (IsCoinBase())
+ return true;
+
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ {
+ // First try finding the previous transaction in database
+ CTransaction txPrev;
+ CTxIndex txindex;
+ if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ continue; // previous transaction not in main chain
+ if (nTime < txPrev.nTime)
+ return false; // Transaction timestamp violation
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ return false; // unable to read block of previous transaction
+ if (block.GetBlockTime() + STAKE_MIN_AGE > nTime)
+ continue; // only count coins meeting min age requirement
+
+ int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+ bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT;
+
+ if (fDebug && GetBoolArg("-printcoinage"))
+ printf("coin age nValueIn=%-12I64d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str());
+ }
+
+ CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
+ if (fDebug && GetBoolArg("-printcoinage"))
+ printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str());
+ nCoinAge = bnCoinDay.getuint64();
+ return true;
+}
+
+// ppcoin: total coin age spent in block, in the unit of coin-days.
+bool CBlock::GetCoinAge(uint64& nCoinAge) const
+{
+ nCoinAge = 0;
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(const CTransaction& tx, vtx)
+ {
+ uint64 nTxCoinAge;
+ if (tx.GetCoinAge(txdb, nTxCoinAge))
+ nCoinAge += nTxCoinAge;
+ else
+ return false;
+ }
+
+ if (nCoinAge == 0) // block coin age minimum 1 coin-day
+ nCoinAge = 1;
+ if (fDebug && GetBoolArg("-printcoinage"))
+ printf("block coin age total nCoinDays=%"PRI64d"\n", nCoinAge);
+ return true;
+}
+
+
bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
{
// Check for duplicate
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+ if (pindexNew->fProofOfStake)
+ setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
+
pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
if (miPrev != mapBlockIndex.end())
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
- pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork();
+
+ // ppcoin: compute chain trust score
+ uint64 nCoinAge;
+ if (!GetCoinAge(nCoinAge))
+ return error("AddToBlockIndex() : invalid transaction in block");
+ pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + nCoinAge;
CTxDB txdb;
if (!txdb.TxnBegin())
return false;
// New best
- if (pindexNew->bnChainWork > bnBestChainWork)
+ if (pindexNew->nChainTrust > nBestChainTrust)
if (!SetBestChain(txdb, pindexNew))
return false;
return DoS(100, error("CheckBlock() : size limits failed"));
// Check proof of work matches claimed amount
- if (!CheckProofOfWork(GetHash(), nBits))
+ if (IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return DoS(50, error("CheckBlock() : proof of work failed"));
// Check timestamp
- if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
+ if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift)
return error("CheckBlock() : block timestamp too far in the future");
// First transaction must be coinbase, the rest must not be
if (vtx[i].IsCoinBase())
return DoS(100, error("CheckBlock() : more than one coinbase"));
+ // ppcoin: only the second transaction can be the optional coinstake
+ for (int i = 2; i < vtx.size(); i++)
+ if (vtx[i].IsCoinStake())
+ return DoS(100, error("CheckBlock() : coinstake in wrong position"));
+
+ // ppcoin: coinbase output should be empty if proof-of-stake block
+ if (IsProofOfStake() && !vtx[0].vout[0].IsEmpty())
+ return error("CheckBlock() : coinbase output not empty for proof-of-stake block");
+
+ // Check coinbase timestamp
+ if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift)
+ return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
+
+ // Check coinstake timestamp
+ if (IsProofOfStake() && GetBlockTime() > (int64)vtx[1].nTime + nMaxClockDrift)
+ return DoS(50, error("CheckBlock() : coinstake timestamp is too early"));
+
// Check transactions
BOOST_FOREACH(const CTransaction& tx, vtx)
+ {
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
+ // ppcoin: check transaction timestamp
+ if (GetBlockTime() < (int64)tx.nTime)
+ return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp"));
+ }
// Check for duplicate txids. This is caught by ConnectInputs(),
// but catching it earlier avoids a potential DoS attack:
if (hashMerkleRoot != BuildMerkleTree())
return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
+ // ppcoin: check block signature
+ if (!CheckBlockSignature())
+ return DoS(100, error("CheckBlock() : bad block signature"));
+
return true;
}
CBlockIndex* pindexPrev = (*mi).second;
int nHeight = pindexPrev->nHeight+1;
- // Check proof of work
- if (nBits != GetNextWorkRequired(pindexPrev, this))
- return DoS(100, error("AcceptBlock() : incorrect proof of work"));
+ // Check proof-of-work or proof-of-stake
+ if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
+ return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
+ if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime())
return error("AcceptBlock() : block's timestamp is too early");
// Check that all transactions are finalized
if (!tx.IsFinal(nHeight, GetBlockTime()))
return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
- // Check that the block chain matches the known block chain up to a checkpoint
- if (!Checkpoints::CheckBlock(nHeight, hash))
- return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
+ // Check that the block chain matches the known block chain up to a hardened checkpoint
+ if (!Checkpoints::CheckHardened(nHeight, hash))
+ return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight));
+
+ // ppcoin: check that the block satisfies synchronized checkpoint
+ if (!Checkpoints::CheckSync(hash, pindexPrev))
+ return error("AcceptBlock() : rejected by synchronized checkpoint");
// Write block to history file
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
}
+ // ppcoin: check pending sync-checkpoint
+ Checkpoints::AcceptPendingSyncCheckpoint();
+
return true;
}
if (mapOrphanBlocks.count(hash))
return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
+ // ppcoin: check proof-of-stake
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
// Preliminary checks
if (!pblock->CheckBlock())
return error("ProcessBlock() : CheckBlock FAILED");
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
- if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
+ // ppcoin: verify hash target and signature of coinstake tx
+ if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
+ return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
+
+ CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
+ if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
- if (deltaTime < 0)
- {
- if (pfrom)
- pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with timestamp before last checkpoint");
- }
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
- bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
+ bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime));
+
if (bnNewBlock > bnRequired)
{
if (pfrom)
pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with too little proof-of-work");
+ return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
}
}
+ // ppcoin: ask for pending sync-checkpoint if any
+ if (!IsInitialBlockDownload())
+ Checkpoints::AskForPendingSyncCheckpoint(pfrom);
// If don't already have its previous block, shunt it off to holding area until we get it
if (!mapBlockIndex.count(pblock->hashPrevBlock))
{
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
CBlock* pblock2 = new CBlock(*pblock);
+ // ppcoin: check proof-of-stake
+ if (pblock2->IsProofOfStake())
+ {
+ // Limited duplicity on stake: prevents block flood attack
+ // Duplicate stake allowed only when there is orphan child block
+ if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+ return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock2->GetProofOfStake().first.ToString().c_str(), pblock2->GetProofOfStake().second, hash.ToString().c_str());
+ else
+ setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
+ }
mapOrphanBlocks.insert(make_pair(hash, pblock2));
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
// Ask this guy to fill in what we're missing
if (pfrom)
+ {
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
+ // ppcoin: getblocks may not obtain the ancestor block rejected
+ // earlier by duplicate-stake check so we ask for it again directly
+ pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+ }
return true;
}
if (pblockOrphan->AcceptBlock())
vWorkQueue.push_back(pblockOrphan->GetHash());
mapOrphanBlocks.erase(pblockOrphan->GetHash());
+ setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake());
delete pblockOrphan;
}
mapOrphanBlocksByPrev.erase(hashPrev);
return true;
}
+// ppcoin: sign block
+bool CBlock::SignBlock(const CKeyStore& keystore)
+{
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
+ if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+ return false;
+ if (whichType == TX_PUBKEY)
+ {
+ // Sign
+ const valtype& vchPubKey = vSolutions[0];
+ CKey key;
+ if (!keystore.GetKey(Hash160(vchPubKey), key))
+ return false;
+ if (key.GetPubKey() != vchPubKey)
+ return false;
+ return key.Sign(GetHash(), vchBlockSig);
+ }
+ return false;
+}
+
+// ppcoin: check block signature
+bool CBlock::CheckBlockSignature() const
+{
+ if (GetHash() == hashGenesisBlock)
+ return vchBlockSig.empty();
+
+ vector<valtype> vSolutions;
+ txnouttype whichType;
+ const CTxOut& txout = IsProofOfStake()? vtx[1].vout[1] : vtx[0].vout[0];
+
+ if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+ return false;
+ if (whichType == TX_PUBKEY)
+ {
+ const valtype& vchPubKey = vSolutions[0];
+ CKey key;
+ if (!key.SetPubKey(vchPubKey))
+ return false;
+ if (vchBlockSig.empty())
+ return false;
+ return key.Verify(GetHash(), vchBlockSig);
+ }
+ return false;
+}
{
if (fTestNet)
{
- hashGenesisBlock = uint256("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
+ hashGenesisBlock = hashGenesisBlockTestNet;
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 28);
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xbf;
// vMerkleTree: 4a5e1e
// Genesis block
- const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
+ const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
CTransaction txNew;
+ txNew.nTime = 1339538219;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
- txNew.vout[0].nValue = 50 * COIN;
- txNew.vout[0].scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
+ txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
- block.nTime = 1231006505;
- block.nBits = 0x1d00ffff;
- block.nNonce = 2083236893;
+ block.nTime = 1339540307;
+ block.nBits = bnProofOfWorkLimit.GetCompact();
+ block.nNonce = 1281822831;
if (fTestNet)
{
printf("%s\n", block.GetHash().ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str());
- assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
+ assert(block.hashMerkleRoot == uint256("0x1557f46a17fcf8843dbe4c0c0edfd1d17eeff2c3c48d73a59d11f5d176e4b54d"));
block.print();
assert(block.GetHash() == hashGenesisBlock);
+ assert(block.CheckBlock());
// Start new block file
unsigned int nFile;
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
+
+ // ppcoin: initialize synchronized checkpoint
+ if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock))
+ return error("LoadBlockIndex() : failed to init sync checkpoint");
+ }
+
+ // ppcoin: if checkpoint master key changed must reset sync-checkpoint
+ {
+ CTxDB txdb;
+ string strPubKey = "";
+ if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
+ {
+ // write checkpoint master key to db
+ txdb.TxnBegin();
+ if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+ return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
+ if (!txdb.TxnCommit())
+ return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
+ if (!Checkpoints::ResetSyncCheckpoint())
+ return error("LoadBlockIndex() : failed to reset sync-checkpoint");
+ }
+ txdb.Close();
}
return true;
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (%u,%u) %s %s tx %d",
+ printf("%d (%u,%u) %s %08lx %s tx %d",
pindex->nHeight,
pindex->nFile,
pindex->nBlockPos,
block.GetHash().ToString().substr(0,20).c_str(),
+ block.nBits,
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size());
}
// Longer invalid proof-of-work chain
- if (pindexBest && bnBestInvalidWork > bnBestChainWork + pindexBest->GetBlockWork() * 6)
+ if (pindexBest && nBestInvalidTrust > nBestChainTrust + pindexBest->GetBlockTrust() * 6)
{
nPriority = 2000;
strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
}
+ if (Checkpoints::hashInvalidCheckpoint != 0)
+ {
+ nPriority = 3000;
+ strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.";
+ }
+
// Alerts
{
LOCK(cs_mapAlerts);
return true;
}
+ // ppcoin: record my external IP reported by peer
+ if (addrFrom.IsRoutable() && addrMe.IsRoutable())
+ addrSeenByPeer = addrMe;
+
// Be shy and don't send version until we hear
if (pfrom->fInbound)
pfrom->PushVersion();
item.second.RelayTo(pfrom);
}
+ // ppcoin: relay sync-checkpoint
+ {
+ LOCK(Checkpoints::cs_hashSyncCheckpoint);
+ if (!Checkpoints::checkpointMessage.IsNull())
+ Checkpoints::checkpointMessage.RelayTo(pfrom);
+ }
+
pfrom->fSuccessfullyConnected = true;
printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
cPeerBlockCounts.input(pfrom->nStartingHeight);
+
+ // ppcoin: ask for pending sync-checkpoint if any
+ if (!IsInitialBlockDownload())
+ Checkpoints::AskForPendingSyncCheckpoint(pfrom);
}
}
}
+ else if (strCommand == "checkpoint")
+ {
+ CSyncCheckpoint checkpoint;
+ vRecv >> checkpoint;
+
+ if (checkpoint.ProcessSyncCheckpoint(pfrom))
+ {
+ // Relay
+ pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ checkpoint.RelayTo(pnode);
+ }
+ }
else
{
uint64 nLastBlockTx = 0;
uint64 nLastBlockSize = 0;
-CBlock* CreateNewBlock(CReserveKey& reservekey)
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
{
- CBlockIndex* pindexPrev = pindexBest;
+ CReserveKey reservekey(pwallet);
// Create new block
auto_ptr<CBlock> pblock(new CBlock());
// Add our coinbase tx as first transaction
pblock->vtx.push_back(txNew);
+ // ppcoin: if coinstake available add coinstake tx
+ static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60; // only initialized at startup
+ CBlockIndex* pindexPrev = pindexBest;
+
+ if (!fProofOfWorkOnly)
+ {
+ while (nLastCoinStakeCheckTime < GetAdjustedTime())
+ {
+ pindexPrev = pindexBest; // get best block again to avoid getting stale
+ pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+ CTransaction txCoinStake;
+ {
+ static CCriticalSection cs;
+ LOCK(cs);
+ // mining may have been suspended for a while so
+ // need to take max to satisfy the timestamp protocol
+ nLastCoinStakeCheckTime++;
+ nLastCoinStakeCheckTime = max(nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
+ txCoinStake.nTime = nLastCoinStakeCheckTime;
+ }
+ if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+ {
+ pblock->vtx.push_back(txCoinStake);
+ pblock->vtx[0].vout[0].SetEmpty();
+ break;
+ }
+ }
+ }
+
+ pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
+
// Collect memory pool transactions into the block
int64 nFees = 0;
{
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
{
CTransaction& tx = (*mi).second;
- if (tx.IsCoinBase() || !tx.IsFinal())
+ if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal())
continue;
COrphan* porphan = NULL;
while (!mapPriority.empty())
{
// Take highest priority transaction off priority queue
- double dPriority = -(*mapPriority.begin()).first;
CTransaction& tx = *(*mapPriority.begin()).second;
mapPriority.erase(mapPriority.begin());
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
- // Transaction fee required depends on block size
- bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
- int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
+ // Timestamp limit
+ if (tx.nTime > GetAdjustedTime())
+ continue;
+
+ // ppcoin: simplify transaction fee - allow free = false
+ int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
// Connecting shouldn't fail due to dependency on other memory pool transactions
// because we're already processing them in order of dependency
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
- if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
+ if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
continue;
mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
swap(mapTestPool, mapTestPoolTmp);
printf("CreateNewBlock(): total size %lu\n", nBlockSize);
}
- pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
+ if (pblock->IsProofOfWork())
+ pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
+ pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
pblock->UpdateTime(pindexPrev);
- pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get());
pblock->nNonce = 0;
return pblock.release();
uint256 hash = pblock->GetHash();
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- if (hash > hashTarget)
- return false;
+ if (hash > hashTarget && pblock->IsProofOfWork())
+ return error("BitcoinMiner : proof-of-work not meeting target");
//// debug print
printf("BitcoinMiner:\n");
- printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+ printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
pblock->print();
printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
CBlockIndex* pindexPrev = pindexBest;
- auto_ptr<CBlock> pblock(CreateNewBlock(reservekey));
+ auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
if (!pblock.get())
return;
+
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
+ // ppcoin: if proof-of-stake block found then process block
+ if (pblock->IsProofOfStake())
+ {
+ if (!pblock->SignBlock(*pwalletMain))
+ {
+ error("BitcoinMiner: Unable to sign new proof-of-stake block");
+ return;
+ }
+ printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str());
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+ CheckWork(pblock.get(), *pwalletMain, reservekey);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ continue;
+ }
+
printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1);
unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4);
- unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8);
unsigned int& nBlockNonce = *(unsigned int*)(pdata + 64 + 12);
// Found a solution
pblock->nNonce = ByteReverse(nNonceFound);
assert(hash == pblock->GetHash());
+ if (!pblock->SignBlock(*pwalletMain))
+ {
+ error("BitcoinMiner: Unable to sign new proof-of-work block");
+ return;
+ }
SetThreadPriority(THREAD_PRIORITY_NORMAL);
CheckWork(pblock.get(), *pwalletMain, reservekey);
break;
// Update nTime every few seconds
+ pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+ pblock->nTime = max(pblock->GetBlockTime(), pblock->GetMaxTransactionTime());
+ pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift);
pblock->UpdateTime(pindexPrev);
nBlockTime = ByteReverse(pblock->nTime);
- if (fTestNet)
- {
- // Changing pblock->nTime can change work required on testnet:
- nBlockBits = ByteReverse(pblock->nBits);
- hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
- }
+ if (pblock->GetBlockTime() >= (int64)pblock->vtx[0].nTime + nMaxClockDrift)
+ break; // need to update coinbase timestamp
}
}
}
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MAIN_H
class CBlockIndex;
class CKeyItem;
class CReserveKey;
+class COutPoint;
class CAddress;
class CInv;
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
-static const int64 MIN_TX_FEE = 50000;
+static const int64 MIN_TX_FEE = 10000;
static const int64 MIN_RELAY_TX_FEE = 10000;
-static const int64 MAX_MONEY = 21000000 * COIN;
+static const int64 MAX_MONEY = 2000000000 * COIN;
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
static const int COINBASE_MATURITY = 100;
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
static const int fHaveUPnP = false;
#endif
+static const uint256 hashGenesisBlockOfficial("0x000000007c82d1f0aa2896b01bf533a8cc26a1f44790be4ceb4ecde7bee24add");
+static const uint256 hashGenesisBlockTestNet("0x00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008");
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
+extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
extern uint256 hashGenesisBlock;
extern CBlockIndex* pindexGenesisBlock;
extern int nBestHeight;
-extern CBigNum bnBestChainWork;
-extern CBigNum bnBestInvalidWork;
+extern uint64 nBestChainTrust;
+extern uint64 nBestInvalidTrust;
extern uint256 hashBestChain;
extern CBlockIndex* pindexBest;
extern unsigned int nTransactionsUpdated;
extern int64 nTimeBestReceived;
extern CCriticalSection cs_setpwalletRegistered;
extern std::set<CWallet*> setpwalletRegistered;
+extern std::map<uint256, CBlock*> mapOrphanBlocks;
// Settings
extern int64 nTransactionFee;
+extern int64 nBalanceReserve;
bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, bool fSendTrickle);
void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
-CBlock* CreateNewBlock(CReserveKey& reservekey);
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly=false);
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
+int64 GetProofOfStakeReward(int64 nCoinAge);
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
-
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew);
+uint256 WantedByOrphan(const CBlock* pblockOrphan);
+const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
return (nValue == -1);
}
+ void SetEmpty()
+ {
+ nValue = 0;
+ scriptPubKey.clear();
+ }
+
+ bool IsEmpty() const
+ {
+ return (nValue == 0 && scriptPubKey.empty());
+ }
+
uint256 GetHash() const
{
return SerializeHash(*this);
std::string ToString() const
{
+ if (IsEmpty()) return "CTxOut(empty)";
if (scriptPubKey.size() < 6)
return "CTxOut(error)";
- return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
+ return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", FormatMoney(nValue).c_str(), scriptPubKey.ToString().substr(0,30).c_str());
}
void print() const
{
public:
int nVersion;
+ unsigned int nTime;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
unsigned int nLockTime;
(
READWRITE(this->nVersion);
nVersion = this->nVersion;
+ READWRITE(nTime);
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
void SetNull()
{
nVersion = 1;
+ nTime = GetAdjustedTime();
vin.clear();
vout.clear();
nLockTime = 0;
bool IsCoinBase() const
{
- return (vin.size() == 1 && vin[0].prevout.IsNull());
+ return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() == 1);
+ }
+
+ bool IsCoinStake() const
+ {
+ // ppcoin: the coin stake transaction is marked with the first output empty
+ return (vin.size() > 0 && vout.size() == 2 && vout[0].IsEmpty());
}
/** Check for standard transaction types
return dPriority > COIN * 144 / 250;
}
- int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const
+ int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=false, enum GetMinFee_mode mode=GMF_BLOCK) const
{
// Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return (a.nVersion == b.nVersion &&
+ a.nTime == b.nTime &&
a.vin == b.vin &&
a.vout == b.vout &&
a.nLockTime == b.nLockTime);
std::string ToString() const
{
std::string str;
- str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
+ str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
+ str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
GetHash().ToString().substr(0,10).c_str(),
+ nTime,
nVersion,
vin.size(),
vout.size(),
@param[in] fStrictPayToScriptHash true if fully validating p2sh transactions
@return Returns true if all checks succeed
*/
- bool ConnectInputs(MapPrevTx inputs,
+ bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
bool ClientConnectInputs();
bool CheckTransaction() const;
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
+ bool GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const; // ppcoin: get transaction coin age
+ bool CheckProofOfStake(unsigned int nBits) const;
protected:
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
// network and disk
std::vector<CTransaction> vtx;
+ // ppcoin: block signature - signed by coin base txout[0]'s owner
+ std::vector<unsigned char> vchBlockSig;
+
// memory only
mutable std::vector<uint256> vMerkleTree;
READWRITE(nBits);
READWRITE(nNonce);
- // ConnectBlock depends on vtx being last so it can calculate offset
+ // ConnectBlock depends on vtx following header to generate CDiskTxPos
if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
+ {
READWRITE(vtx);
+ READWRITE(vchBlockSig);
+ }
else if (fRead)
+ {
const_cast<CBlock*>(this)->vtx.clear();
+ const_cast<CBlock*>(this)->vchBlockSig.clear();
+ }
)
void SetNull()
nBits = 0;
nNonce = 0;
vtx.clear();
+ vchBlockSig.clear();
vMerkleTree.clear();
nDoS = 0;
}
void UpdateTime(const CBlockIndex* pindexPrev);
+ // ppcoin: two types of block: proof-of-work or proof-of-stake
+ bool IsProofOfStake() const
+ {
+ return (vtx.size() > 1 && vtx[1].IsCoinStake());
+ }
+
+ bool IsProofOfWork() const
+ {
+ return !IsProofOfStake();
+ }
+
+ std::pair<COutPoint, unsigned int> GetProofOfStake() const
+ {
+ return IsProofOfStake()? std::make_pair(vtx[1].vin[0].prevout, vtx[1].nTime) : std::make_pair(COutPoint(), (unsigned int)0);
+ }
+
+ // ppcoin: get max transaction timestamp
+ int64 GetMaxTransactionTime() const
+ {
+ int64 maxTransactionTime = 0;
+ BOOST_FOREACH(const CTransaction& tx, vtx)
+ maxTransactionTime = std::max(maxTransactionTime, (int64)tx.nTime);
+ return maxTransactionTime;
+ }
uint256 BuildMerkleTree() const
{
}
// Check the header
- if (!CheckProofOfWork(GetHash(), nBits))
+ if (fReadTransactions && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits))
return error("CBlock::ReadFromDisk() : errors in block header");
return true;
void print() const
{
- printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d)\n",
+ printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%d, vchBlockSig=%s)\n",
GetHash().ToString().substr(0,20).c_str(),
nVersion,
hashPrevBlock.ToString().substr(0,20).c_str(),
hashMerkleRoot.ToString().substr(0,10).c_str(),
nTime, nBits, nNonce,
- vtx.size());
+ vtx.size(),
+ HexStr(vchBlockSig.begin(), vchBlockSig.end()).c_str());
for (unsigned int i = 0; i < vtx.size(); i++)
{
printf(" ");
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
bool CheckBlock() const;
bool AcceptBlock();
+ bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block
+ bool SignBlock(const CKeyStore& keystore);
+ bool CheckBlockSignature() const;
private:
bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
CBlockIndex* pnext;
unsigned int nFile;
unsigned int nBlockPos;
+ uint64 nChainTrust;// ppcoin: trust score of chain, in the unit of coin-days
int nHeight;
- CBigNum bnChainWork;
+ bool fProofOfStake; // ppcoin: is the block of proof-of-stake type
+ COutPoint prevoutStake;
+ unsigned int nStakeTime;
// block header
int nVersion;
nFile = 0;
nBlockPos = 0;
nHeight = 0;
- bnChainWork = 0;
+ nChainTrust = 0;
+ fProofOfStake = true;
+ prevoutStake.SetNull();
+ nStakeTime = 0;
nVersion = 0;
hashMerkleRoot = 0;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0;
- bnChainWork = 0;
+ nChainTrust = 0;
+ fProofOfStake = block.IsProofOfStake();
+ if (fProofOfStake)
+ {
+ prevoutStake = block.vtx[1].vin[0].prevout;
+ nStakeTime = block.vtx[1].nTime;
+ }
+ else
+ {
+ prevoutStake.SetNull();
+ nStakeTime = 0;
+ }
nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot;
return (int64)nTime;
}
- CBigNum GetBlockWork() const
+ int64 GetBlockTrust() const
{
- CBigNum bnTarget;
- bnTarget.SetCompact(nBits);
- if (bnTarget <= 0)
- return 0;
- return (CBigNum(1)<<256) / (bnTarget+1);
+ return (nChainTrust - (pprev? pprev->nChainTrust : 0));
}
bool IsInMainChain() const
bool CheckIndex() const
{
- return CheckProofOfWork(GetBlockHash(), nBits);
+ return IsProofOfWork() ? CheckProofOfWork(GetBlockHash(), nBits) : true;
}
bool EraseBlockFromDisk()
return pindex->GetMedianTimePast();
}
+ bool IsProofOfWork() const
+ {
+ return !fProofOfStake;
+ }
+ bool IsProofOfStake() const
+ {
+ return fProofOfStake;
+ }
std::string ToString() const
{
- return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
- pprev, pnext, nFile, nBlockPos, nHeight,
+ return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nChainTrust=%"PRI64d" nHeight=%d, fProofOfStake=%d prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
+ pprev, pnext, nFile, nBlockPos, nChainTrust, nHeight,
+ fProofOfStake, prevoutStake.ToString().c_str(), nStakeTime,
hashMerkleRoot.ToString().substr(0,10).c_str(),
GetBlockHash().ToString().substr(0,20).c_str());
}
READWRITE(hashNext);
READWRITE(nFile);
READWRITE(nBlockPos);
+ READWRITE(nChainTrust);
READWRITE(nHeight);
+ READWRITE(fProofOfStake);
+ if (fProofOfStake)
+ {
+ READWRITE(prevoutStake);
+ READWRITE(nStakeTime);
+ }
+ else if (fRead)
+ {
+ const_cast<CDiskBlockIndex*>(this)->prevoutStake.SetNull();
+ const_cast<CDiskBlockIndex*>(this)->nStakeTime = 0;
+ }
// block header
READWRITE(this->nVersion);
bool CheckSignature()
{
CKey key;
- if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284")))
+ if (!key.SetPubKey(ParseHex("0487ca85b6ae9d311f996c7616d20d0c88a5b4f07d25e78f419019f35cce6522acf978b2d99f0e7a58db1f120439e5c1889266927854aa57c93956c2569188a539")))
return error("CAlert::CheckSignature() : SetPubKey failed");
if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
return error("CAlert::CheckSignature() : verify signature failed");
# Copyright (c) 2009-2010 Satoshi Nakamoto
+# Copyright (c) 2012 The PPCoin developers
# Distributed under the MIT/X11 software license, see the accompanying
# file license.txt or http://www.opensource.org/licenses/mit-license.php.
+# Windows commandline build procedure:
+# - Install MinGW following http://www.mingw.org/wiki/Getting_Started.
+# Install with the C++ and MSYS options checked
+#
+# - Add/edit 'MAKE' environment variable with value '/c/MinGW32/bin/mingw32-make.exe'
+#
+# - Build openssl library version: 1.0.1b
+# download from http://www.openssl.org/source/
+# Extract to c:\openssl-1.0.1b-mgw
+# In MinGW MSYS:
+# ./config
+# make
+#
+# - Build Berkeley DB library version: 4.8.30.NC
+# download from http://www.oracle.com/technology/software/products/berkeley-db/index.html
+# Extract to c:\db-4.8.30.NC-mgw
+# In MinGW MSYS:
+# cd build_unix
+# sh ../dist/configure --disable-replication --enable-mingw --enable-cxx --prefix=/usr/local
+# Edit db.h@113 in build_unix
+# from
+# typedef pthread_t db_threadid_t;
+# to
+# typedef u_int32_t db_threadid_t;
+# Then
+# make
+#
+# - Build Boost C++ library version: 1.47.0
+# download from http://www.boost.org/users/download/
+# Extract to c:\boost-1.47.0-mgw
+# Install Boost.Build:
+# cd tools\build\v2
+# bootstrap.bat
+# b2 install --prefix=BOOST_BUILD_INSTALL_DIR
+# Add BOOST_BUILD_INSTALL_DIR to your PATH system environment variable
+# Build boost library in MSDOS:
+# cd c:\boost-1.47.0-mgw
+# bjam toolset=gcc --build-type=complete stage
+#
+# - Build ppcoind.exe
+# in MinGW MSYS
+# cd ppcoin/src
+# make ppcoind.exe -f makefile.mingw USE_UPNP=
+#
+#
+
USE_UPNP:=0
INCLUDEPATHS= \
-L"C:\openssl-1.0.1b-mgw"
LIBS= \
- -l boost_system-mgw45-mt-s-1_47 \
- -l boost_filesystem-mgw45-mt-s-1_47 \
- -l boost_program_options-mgw45-mt-s-1_47 \
- -l boost_thread-mgw45-mt-s-1_47 \
+ -l boost_system-mgw46-mt-s-1_47 \
+ -l boost_filesystem-mgw46-mt-s-1_47 \
+ -l boost_program_options-mgw46-mt-s-1_47 \
+ -l boost_thread-mgw46-mt-s-1_47 \
-l db_cxx \
-l ssl \
-l crypto
obj/noui.o
-all: bitcoind.exe
+all: ppcoind.exe
obj/%.o: %.cpp $(HEADERS)
g++ -c $(CFLAGS) -o $@ $<
-bitcoind.exe: $(OBJS:obj/%=obj/%)
+ppcoind.exe: $(OBJS:obj/%=obj/%)
g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS)
TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
g++ $(CFLAGS) -o $@ $(LIBPATHS) $^ -lboost_unit_test_framework $(LIBS)
clean:
- -del /Q bitcoind test_bitcoin
+ -del /Q ppcoind test_bitcoin
-del /Q obj\*
-del /Q obj-test\*
-del /Q build.h
# 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.
obj/noui.o
-all: bitcoind
+all: ppcoind
# auto-generated dependencies:
-include obj/*.P
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
rm -f $(@:%.o=%.d)
-bitcoind: $(OBJS:obj/%=obj/%)
- $(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+ppcoind: $(OBJS:obj/%=obj/%)
+ $(CXX) $(xCXXFLAGS) -rdynamic -o $@ $^ $(LDFLAGS) $(LIBS)
TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
rm -f $(@:%.o=%.d)
-test_bitcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
+test_ppcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
$(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
clean:
- -rm -f bitcoind test_bitcoin
+ -rm -f ppcoind test_ppcoin genesis
-rm -f obj/*.o
-rm -f obj-test/*.o
-rm -f obj/*.P
-rm -f obj-test/*.P
-rm -f src/build.h
+ -rm -f ppcoin/obj/*
+
+ppcoin/obj/genesis.o: ppcoin/genesis.cpp
+ $(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
+ $(CXX) -c $(xCXXFLAGS) -MMD -DPPCOIN_GENESIS -o obj/nogui/init.o init.cpp
+
+genesis: ppcoin/obj/genesis.o $(OBJS:obj/%=obj/nogui/%)
+ $(CXX) $(CXXFLAGS) -o $@ $^ $(LIBS)
+ -rm -f obj/init.*
+ -rm -f ppcoin/obj/genesis.*
FORCE:
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
static bool fUseUPnP = false;
uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
CAddress addrLocalHost(CService("0.0.0.0", 0), nLocalServices);
+CAddress addrSeenByPeer(CService("0.0.0.0", 0), nLocalServices);
static CNode* pnodeLocalHost = NULL;
uint64 nLocalHostNonce = 0;
array<int, THREAD_MAX> vnThreadsRunning;
void ThreadGetMyExternalIP(void* parg)
{
- // Wait for IRC to get it first
- if (GetBoolArg("-irc", false))
+ // Wait for IRC to get it first - disabled with ppcoin
+ if (false && GetBoolArg("-irc", false))
{
for (int i = 0; i < 2 * 60; i++)
{
// Each pair gives a source name and a seed name.
// The first name is used as information source for addrman.
// The second name should resolve to a list of seed addresses.
+// testnet dns seed begins with 't', all else are ppcoin dns seeds.
static const char *strDNSSeed[][2] = {
- {"xf2.org", "bitseed.xf2.org"},
- {"bluematt.me", "dnsseed.bluematt.me"},
- {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"},
- {"dashjr.org", "dnsseed.bitcoin.dashjr.org"},
+ {"ppcseed", "ppcseed.zapto.org"},
+ {"tncseed", "tncseed.zapto.org"},
};
void ThreadDNSAddressSeed(void* parg)
printf("ThreadDNSAddressSeed started\n");
int found = 0;
- if (!fTestNet)
+ if (true /*!fTestNet*/) // ppcoin enables dns seeding with testnet too
{
printf("Loading addresses from DNS seeds (could take a while)\n");
for (unsigned int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
+ if (fTestNet && strDNSSeed[seed_idx][1][0] != 't') continue;
+ if ((!fTestNet) && strDNSSeed[seed_idx][1][0] == 't') continue;
+
vector<CNetAddr> vaddr;
vector<CAddress> vAdd;
if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
unsigned int pnSeed[] =
{
- 0x959bd347, 0xf8de42b2, 0x73bc0518, 0xea6edc50, 0x21b00a4d, 0xc725b43d, 0xd665464d, 0x1a2a770e,
- 0x27c93946, 0x65b2fa46, 0xb80ae255, 0x66b3b446, 0xb1877a3e, 0x6ee89e3e, 0xc3175b40, 0x2a01a83c,
- 0x95b1363a, 0xa079ad3d, 0xe6ca801f, 0x027f4f4a, 0x34f7f03a, 0xf790f04a, 0x16ca801f, 0x2f4d5e40,
- 0x3a4d5e40, 0xc43a322e, 0xc8159753, 0x14d4724c, 0x7919a118, 0xe0bdb34e, 0x68a16b2e, 0xff64b44d,
- 0x6099115b, 0x9b57b05b, 0x7bd1b4ad, 0xdf95944f, 0x29d2b73d, 0xafa8db79, 0xe247ba41, 0x24078348,
- 0xf722f03c, 0x33567ebc, 0xace64ed4, 0x984d3932, 0xb5f34e55, 0x27b7024d, 0x94579247, 0x8894042e,
- 0x9357d34c, 0x1063c24b, 0xcaa228b1, 0xa3c5a8b2, 0x5dc64857, 0xa2c23643, 0xa8369a54, 0x31203077,
- 0x00707c5c, 0x09fc0b3a, 0x272e9e2e, 0xf80f043e, 0x9449ca3e, 0x5512c33e, 0xd106b555, 0xe8024157,
- 0xe288ec29, 0xc79c5461, 0xafb63932, 0xdb02ab4b, 0x0e512777, 0x8a145a4c, 0xb201ff4f, 0x5e09314b,
- 0xcd9bfbcd, 0x1c023765, 0x4394e75c, 0xa728bd4d, 0x65331552, 0xa98420b1, 0x89ecf559, 0x6e80801f,
- 0xf404f118, 0xefd62b51, 0x05918346, 0x9b186d5f, 0xacabab46, 0xf912e255, 0xc188ea62, 0xcc55734e,
- 0xc668064d, 0xd77a4558, 0x46201c55, 0xf17dfc80, 0xf7142f2e, 0x87bfb718, 0x8aa54fb2, 0xc451d518,
- 0xc4ae8831, 0x8dd44d55, 0x5bbd206c, 0x64536b5d, 0x5c667e60, 0x3b064242, 0xfe963a42, 0xa28e6dc8,
- 0xe8a9604a, 0xc989464e, 0xd124a659, 0x50065140, 0xa44dfe5e, 0x1079e655, 0x3fb986d5, 0x47895b18,
- 0x7d3ce4ad, 0x4561ba50, 0x296eec62, 0x255b41ad, 0xaed35ec9, 0x55556f12, 0xc7d3154d, 0x3297b65d,
- 0x8930121f, 0xabf42e4e, 0x4a29e044, 0x1212685d, 0x676c1e40, 0xce009744, 0x383a8948, 0xa2dbd0ad,
- 0xecc2564d, 0x07dbc252, 0x887ee24b, 0x5171644c, 0x6bb798c1, 0x847f495d, 0x4cbb7145, 0x3bb81c32,
- 0x45eb262e, 0xc8015a4e, 0x250a361b, 0xf694f946, 0xd64a183e, 0xd4f1dd59, 0x8f20ffd4, 0x51d9e55c,
- 0x09521763, 0x5e02002e, 0x32c8074d, 0xe685762e, 0x8290b0bc, 0x762a922e, 0xfc5ee754, 0x83a24829,
- 0x775b224d, 0x6295bb4d, 0x38ec0555, 0xbffbba50, 0xe5560260, 0x86b16a7c, 0xd372234e, 0x49a3c24b,
- 0x2f6a171f, 0x4d75ed60, 0xae94115b, 0xcb543744, 0x63080c59, 0x3f9c724c, 0xc977ce18, 0x532efb18,
- 0x69dc3b2e, 0x5f94d929, 0x1732bb4d, 0x9c814b4d, 0xe6b3762e, 0xc024f662, 0x8face35b, 0x6b5b044d,
- 0x798c7b57, 0x79a6b44c, 0x067d3057, 0xf9e94e5f, 0x91cbe15b, 0x71405eb2, 0x2662234e, 0xcbcc4a6d,
- 0xbf69d54b, 0xa79b4e55, 0xec6d3e51, 0x7c0b3c02, 0x60f83653, 0x24c1e15c, 0x1110b62e, 0x10350f59,
- 0xa56f1d55, 0x3509e7a9, 0xeb128354, 0x14268e2e, 0x934e28bc, 0x8e32692e, 0x8331a21f, 0x3e633932,
- 0xc812b12e, 0xc684bf2e, 0x80112d2e, 0xe0ddc96c, 0xc630ca4a, 0x5c09b3b2, 0x0b580518, 0xc8e9d54b,
- 0xd169aa43, 0x17d0d655, 0x1d029963, 0x7ff87559, 0xcb701f1f, 0x6fa3e85d, 0xe45e9a54, 0xf05d1802,
- 0x44d03b2e, 0x837b692e, 0xccd4354e, 0x3d6da13c, 0x3423084d, 0xf707c34a, 0x55f6db3a, 0xad26e442,
- 0x6233a21f, 0x09e80e59, 0x8caeb54d, 0xbe870941, 0xb407d20e, 0x20b51018, 0x56fb152e, 0x460d2a4e,
- 0xbb9a2946, 0x560eb12e, 0xed83dd29, 0xd6724f53, 0xa50aafb8, 0x451346d9, 0x88348e2e, 0x7312fead,
- 0x8ecaf96f, 0x1bda4e5f, 0xf1671e40, 0x3c8c3e3b, 0x4716324d, 0xdde24ede, 0xf98cd17d, 0xa91d4644,
- 0x28124eb2, 0x147d5129, 0xd022042e, 0x61733d3b, 0xad0d5e02, 0x8ce2932e, 0xe5c18502, 0x549c1e32,
- 0x9685801f, 0x86e217ad, 0xd948214b, 0x4110f462, 0x3a2e894e, 0xbd35492e, 0x87e0d558, 0x64b8ef7d,
- 0x7c3eb962, 0x72a84b3e, 0x7cd667c9, 0x28370a2e, 0x4bc60e7b, 0x6fc1ec60, 0x14a6983f, 0x86739a4b,
- 0x46954e5f, 0x32e2e15c, 0x2e9326cf, 0xe5801c5e, 0x379607b2, 0x32151145, 0xf0e39744, 0xacb54c55,
- 0xa37dfb60, 0x83b55cc9, 0x388f7ca5, 0x15034f5f, 0x3e94965b, 0x68e0ffad, 0x35280f59, 0x8fe190cf,
- 0x7c6ba5b2, 0xa5e9db43, 0x4ee1fc60, 0xd9d94e5f, 0x04040677, 0x0ea9b35e, 0x5961f14f, 0x67fda063,
- 0xa48a5a31, 0xc6524e55, 0x283d325e, 0x3f37515f, 0x96b94b3e, 0xacce620e, 0x6481cc5b, 0xa4a06d4b,
- 0x9e95d2d9, 0xe40c03d5, 0xc2f4514b, 0xb79aad44, 0xf64be843, 0xb2064070, 0xfca00455, 0x429dfa4e,
- 0x2323f173, 0xeda4185e, 0xabd5227d, 0x9efd4d58, 0xb1104758, 0x4811e955, 0xbd9ab355, 0xe921f44b,
- 0x9f166dce, 0x09e279b2, 0xe0c9ac7b, 0x7901a5ad, 0xa145d4b0, 0x79104671, 0xec31e35a, 0x4fe0b555,
- 0xc7d9cbad, 0xad057f55, 0xe94cc759, 0x7fe0b043, 0xe4529f2e, 0x0d4dd4b2, 0x9f11a54d, 0x031e2e4e,
- 0xe6014f5f, 0x11d1ca6c, 0x26bd7f61, 0xeb86854f, 0x4d347b57, 0x116bbe2e, 0xdba7234e, 0x7bcbfd2e,
- 0x174dd4b2, 0x6686762e, 0xb089ba50, 0xc6258246, 0x087e767b, 0xc4a8cb4a, 0x595dba50, 0x7f0ae502,
- 0x7b1dbd5a, 0xa0603492, 0x57d1af4b, 0x9e21ffd4, 0x6393064d, 0x7407376e, 0xe484762e, 0x122a4e53,
- 0x4a37aa43, 0x3888a6be, 0xee77864e, 0x039c8dd5, 0x688d89af, 0x0e988f62, 0x08218246, 0xfc2f8246,
- 0xd1d97040, 0xd64cd4b2, 0x5ae4a6b8, 0x7d0de9bc, 0x8d304d61, 0x06c5c672, 0xa4c8bd4d, 0xe0fd373b,
- 0x575ebe4d, 0x72d26277, 0x55570f55, 0x77b154d9, 0xe214293a, 0xfc740f4b, 0xfe3f6a57, 0xa9c55f02,
- 0xae4054db, 0x2394d918, 0xb511b24a, 0xb8741ab2, 0x0758e65e, 0xc7b5795b, 0xb0a30a4c, 0xaf7f170c,
- 0xf3b4762e, 0x8179576d, 0x738a1581, 0x4b95b64c, 0x9829b618, 0x1bea932e, 0x7bdeaa4b, 0xcb5e0281,
- 0x65618f54, 0x0658474b, 0x27066acf, 0x40556d65, 0x7d204d53, 0xf28bc244, 0xdce23455, 0xadc0ff54,
- 0x3863c948, 0xcee34e5f, 0xdeb85e02, 0x2ed17a61, 0x6a7b094d, 0x7f0cfc40, 0x59603f54, 0x3220afbc,
- 0xb5dfd962, 0x125d21c0, 0x13f8d243, 0xacfefb4e, 0x86c2c147, 0x3d8bbd59, 0xbd02a21f, 0x2593042e,
- 0xc6a17a7c, 0x28925861, 0xb487ed44, 0xb5f4fd6d, 0x90c28a45, 0x5a14f74d, 0x43d71b4c, 0x728ebb5d,
- 0x885bf950, 0x08134dd0, 0x38ec046e, 0xc575684b, 0x50082d2e, 0xa2f47757, 0x270f86ae, 0xf3ff6462,
- 0x10ed3f4e, 0x4b58d462, 0xe01ce23e, 0x8c5b092e, 0x63e52f4e, 0x22c1e85d, 0xa908f54e, 0x8591624f,
- 0x2c0fb94e, 0xa280ba3c, 0xb6f41b4c, 0x24f9aa47, 0x27201647, 0x3a3ea6dc, 0xa14fc3be, 0x3c34bdd5,
- 0x5b8d4f5b, 0xaadeaf4b, 0xc71cab50, 0x15697a4c, 0x9a1a734c, 0x2a037d81, 0x2590bd59, 0x48ec2741,
- 0x53489c5b, 0x7f00314b, 0x2170d362, 0xf2e92542, 0x42c10b44, 0x98f0f118, 0x883a3456, 0x099a932e,
- 0xea38f7bc, 0x644e9247, 0xbb61b62e, 0x30e0863d, 0x5f51be54, 0x207215c7, 0x5f306c45, 0xaa7f3932,
- 0x98da7d45, 0x4e339b59, 0x2e411581, 0xa808f618, 0xad2c0c59, 0x54476741, 0x09e99fd1, 0x5db8f752,
- 0xc16df8bd, 0x1dd4b44f, 0x106edf2e, 0x9e15c180, 0x2ad6b56f, 0x633a5332, 0xff33787c, 0x077cb545,
- 0x6610be6d, 0x75aad2c4, 0x72fb4d5b, 0xe81e0f59, 0x576f6332, 0x47333373, 0x351ed783, 0x2d90fb50,
- 0x8d5e0f6c, 0x5b27a552, 0xdb293ebb, 0xe55ef950, 0x4b133ad8, 0x75df975a, 0x7b6a8740, 0xa899464b,
- 0xfab15161, 0x10f8b64d, 0xd055ea4d, 0xee8e146b, 0x4b14afb8, 0x4bc1c44a, 0x9b961dcc, 0xd111ff43,
- 0xfca0b745, 0xc800e412, 0x0afad9d1, 0xf751c350, 0xf9f0cccf, 0xa290a545, 0x8ef13763, 0x7ec70d59,
- 0x2b066acf, 0x65496c45, 0xade02c1b, 0xae6eb077, 0x92c1e65b, 0xc064e6a9, 0xc649e56d, 0x5287a243,
- 0x36de4f5b, 0x5b1df6ad, 0x65c39a59, 0xdba805b2, 0x20067aa8, 0x6457e56d, 0x3cee26cf, 0xfd3ff26d,
- 0x04f86d4a, 0x06b8e048, 0xa93bcd5c, 0x91135852, 0xbe90a643, 0x8fa0094d, 0x06d8215f, 0x2677094d,
- 0xd735685c, 0x164a00c9, 0x5209ac5f, 0xa9564c5c, 0x3b504f5f, 0xcc826bd0, 0x4615042e, 0x5fe13b4a,
- 0x8c81b86d, 0x879ab68c, 0x1de564b8, 0x434487d8, 0x2dcb1b63, 0x82ab524a, 0xb0676abb, 0xa13d9c62,
- 0xdbb5b86d, 0x5b7f4b59, 0xaddfb44d, 0xad773532, 0x3997054c, 0x72cebd89, 0xb194544c, 0xc5b8046e,
- 0x6e1adeb2, 0xaa5abb51, 0xefb54b44, 0x15efc54f, 0xe9f1bc4d, 0x5f401b6c, 0x97f018ad, 0xc82f9252,
- 0x2cdc762e, 0x8e52e56d, 0x1827175e, 0x9b7d7d80, 0xb2ad6845, 0x51065140, 0x71180a18, 0x5b27006c,
- 0x0621e255, 0x721cbe58, 0x670c0cb8, 0xf8bd715d, 0xe0bdc5d9, 0xed843501, 0x4b84554d, 0x7f1a18bc,
- 0x53bcaf47, 0x5729d35f, 0xf0dda246, 0x22382bd0, 0x4d641fb0, 0x316afcde, 0x50a22f1f, 0x73608046,
- 0xc461d84a, 0xb2dbe247,
+ 0xfc01a8c0,
};
void DumpAddresses()
MapPort(fUseUPnP);
// Get addresses from IRC and advertise ours
- if (!CreateThread(ThreadIRCSeed, NULL))
- printf("Error: CreateThread(ThreadIRCSeed) failed\n");
+ // if (!CreateThread(ThreadIRCSeed, NULL))
+ // printf("Error: CreateThread(ThreadIRCSeed) failed\n");
+ // IRC disabled with ppcoin
+ printf("IRC seeding/communication disabled\n");
// Send and receive from sockets, accept connections
if (!CreateThread(ThreadSocketHandler, NULL))
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NET_H
extern bool fAllowDNS;
extern uint64 nLocalServices;
extern CAddress addrLocalHost;
+extern CAddress addrSeenByPeer;
extern uint64 nLocalHostNonce;
extern boost::array<int, THREAD_MAX> vnThreadsRunning;
extern CAddrMan addrman;
std::set<CAddress> setAddrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
+ uint256 hashCheckpointKnown; // ppcoin: known sent sync-checkpoint
// inventory based relay
mruset<CInv> setInventoryKnown;
nStartingHeight = -1;
fGetAddr = false;
nMisbehavior = 0;
+ hashCheckpointKnown = 0;
setInventoryKnown.max_size(SendBufferSize() / 1000);
// Be shy and don't send version until we hear
--- /dev/null
+// Copyright (c) 2011 The PPCoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+#include "../headers.h"
+#include "../db.h"
+#include "../net.h"
+#include "../init.h"
+#include "../main.h"
+#include "../util.h"
+
+using namespace std;
+using namespace boost;
+
+int main(int argc, char *argv[])
+{
+ fPrintToConsole = true;
+ printf("PPCoin Begin Genesis Block\n");
+
+ // Genesis block
+ const char* pszTimestamp = "MarketWatch 07/Nov/2011 Gold tops $1,790 to end at over six-week high";
+ CTransaction txNew;
+ txNew.vin.resize(1);
+ txNew.vout.resize(1);
+ txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
+ txNew.vout[0].SetEmpty();
+ CBlock block;
+ block.vtx.push_back(txNew);
+ block.hashPrevBlock = 0;
+ block.hashMerkleRoot = block.BuildMerkleTree();
+ block.nVersion = 1;
+ block.nBits = 0x1d00ffff;
+ block.nTime = GetAdjustedTime();
+ block.nNonce = 0;
+
+ CBigNum bnTarget;
+ bnTarget.SetCompact(block.nBits);
+
+ while (block.GetHash() > bnTarget.getuint256())
+ {
+ if ((block.nNonce >> 20) << 20 == block.nNonce)
+ {
+ if (block.vtx[0].nTime + 7200 < GetAdjustedTime() + 60)
+ block.vtx[0].nTime = GetAdjustedTime();
+ block.nTime = GetAdjustedTime();
+ printf("n=%dM hash=%s\n", block.nNonce >> 20,
+ block.GetHash().ToString().c_str());
+ }
+ block.nNonce++;
+ }
+
+ printf("PPCoin Found Genesis Block:\n");
+ printf("genesis hash=%s\n", block.GetHash().ToString().c_str());
+ printf("merkle root=%s\n", block.hashMerkleRoot.ToString().c_str());
+ block.print();
+
+ printf("PPCoin End Genesis Block\n");
+}
--- /dev/null
+*
+!.gitignore
#include <string>
#include "uint256.h"
+#define PPCOIN_PORT 9901
+#define RPC_PORT 9902
+#define TESTNET_PORT 9903
+
extern bool fTestNet;
+
static inline unsigned short GetDefaultPort(const bool testnet = fTestNet)
{
- return testnet ? 18333 : 8333;
+ return testnet ? TESTNET_PORT : PPCOIN_PORT;
}
{
switch(unit)
{
- case BTC: return QString("BTC");
- case mBTC: return QString("mBTC");
- case uBTC: return QString::fromUtf8("μBTC");
+ case BTC: return QString("PPC");
+ case mBTC: return QString("mPPC");
+ case uBTC: return QString::fromUtf8("μPPC");
default: return QString("???");
}
}
{
switch(unit)
{
- case BTC: return QString("Bitcoins");
- case mBTC: return QString("Milli-Bitcoins (1 / 1,000)");
- case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)");
+ case BTC: return QString("PPCoins");
+ case mBTC: return QString("Milli-PPCoins (1 / 1,000)");
+ case uBTC: return QString("Micro-PPCoins (1 / 1,000,000)");
default: return QString("???");
}
}
{
switch(unit)
{
- case BTC: return 100000000;
- case mBTC: return 100000;
- case uBTC: return 100;
- default: return 100000000;
+ case BTC: return 1000000;
+ case mBTC: return 1000;
+ case uBTC: return 1;
+ default: return 1000000;
}
}
{
switch(unit)
{
- case BTC: return 8; // 21,000,000 (# digits, without commas)
- case mBTC: return 11; // 21,000,000,000
- case uBTC: return 14; // 21,000,000,000,000
+ case BTC: return 10; // 21,000,000 (# digits, without commas)
+ case mBTC: return 13; // 21,000,000,000
+ case uBTC: return 16; // 21,000,000,000,000
default: return 0;
}
}
{
switch(unit)
{
- case BTC: return 8;
- case mBTC: return 5;
- case uBTC: return 2;
+ case BTC: return 6;
+ case mBTC: return 3;
+ case uBTC: return 0;
default: return 0;
}
}
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/foreach.hpp>
-typedef vector<unsigned char> valtype;
static const valtype vchFalse(0);
static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
assert(nIn < txTo.vin.size());
CTxIn& txin = txTo.vin[nIn];
assert(txin.prevout.n < txFrom.vout.size());
+ assert(txin.prevout.hash == txFrom.GetHash());
const CTxOut& txout = txFrom.vout[txin.prevout.n];
// Leave out the signature from the hash, since a signature can't sign itself.
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef H_BITCOIN_SCRIPT
#include <boost/foreach.hpp>
+typedef std::vector<unsigned char> valtype;
+
class CTransaction;
class CKeyStore;
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_SERIALIZE_H
-
-
+#ifndef THROW_WITH_STACKTRACE
+#define THROW_WITH_STACKTRACE(exception) \
+{ \
+ LogStackTrace(); \
+ throw (exception); \
+}
+void LogStackTrace();
+#endif
//
// Compact size
nSizeRet = xSize;
}
if (nSizeRet > (uint64)MAX_SIZE)
- throw std::ios_base::failure("ReadCompactSize() : size too large");
+ THROW_WITH_STACKTRACE(std::ios_base::failure("ReadCompactSize() : size too large"));
return nSizeRet;
}
{
state |= bits;
if (state & exceptmask)
- throw std::ios_base::failure(psz);
+ THROW_WITH_STACKTRACE(std::ios_base::failure(psz));
}
bool eof() const { return size() == 0; }
{
state |= bits;
if (state & exceptmask)
- throw std::ios_base::failure(psz);
+ THROW_WITH_STACKTRACE(std::ios_base::failure(psz));
}
bool fail() const { return state & (std::ios::badbit | std::ios::failbit); }
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "shlwapi.h"
#endif
+#ifndef WIN32
+#include <execinfo.h>
+#endif
+
using namespace std;
using namespace boost;
-
+static FILE* fileout = NULL;
inline int OutputDebugStringF(const char* pszFormat, ...)
{
else
{
// print to debug.log
- static FILE* fileout = NULL;
-
if (!fileout)
{
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
int64 n_abs = (n > 0 ? n : -n);
int64 quotient = n_abs/COIN;
int64 remainder = n_abs%COIN;
- string str = strprintf("%"PRI64d".%08"PRI64d, quotient, remainder);
+ string str = strprintf("%"PRI64d".%06"PRI64d, quotient, remainder);
// Right-trim excess 0's before the decimal point:
int nTrim = 0;
throw;
}
+void LogStackTrace() {
+ printf("\n\n******* exception encountered *******\n");
+ if (fileout)
+ {
+#ifndef WIN32
+ void* pszBuffer[32];
+ size_t size;
+ size = backtrace(pszBuffer, 32);
+ backtrace_symbols_fd(pszBuffer, size, fileno(fileout));
+#endif
+ }
+}
+
void PrintExceptionContinue(std::exception* pex, const char* pszThread)
{
char pszMessage[10000];
{
namespace fs = boost::filesystem;
- // Windows: C:\Documents and Settings\username\Application Data\Bitcoin
- // Mac: ~/Library/Application Support/Bitcoin
- // Unix: ~/.bitcoin
+ // Windows: C:\Documents and Settings\username\Application Data\PPCoin
+ // Mac: ~/Library/Application Support/PPCoin
+ // Unix: ~/.ppcoin
#ifdef WIN32
// Windows
- return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "Bitcoin";
+ return MyGetSpecialFolderPath(CSIDL_APPDATA, true) / "PPCoin";
#else
fs::path pathRet;
char* pszHome = getenv("HOME");
// Mac
pathRet /= "Library/Application Support";
fs::create_directory(pathRet);
- return pathRet / "Bitcoin";
+ return pathRet / "PPCoin";
#else
// Unix
- return pathRet / ".bitcoin";
+ return pathRet / ".ppcoin";
#endif
#endif
}
{
namespace fs = boost::filesystem;
- fs::path pathConfigFile(GetArg("-conf", "bitcoin.conf"));
+ fs::path pathConfigFile(GetArg("-conf", "ppcoin.conf"));
if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile;
return pathConfigFile;
}
{
namespace fs = boost::filesystem;
- fs::path pathPidFile(GetArg("-pid", "bitcoind.pid"));
+ fs::path pathPidFile(GetArg("-pid", "ppcoind.pid"));
if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile;
return pathPidFile;
}
if (line.find("Hidden") != string::npos &&
line.find("true") != string::npos)
return false;
+>>>>>>> bitcoin
}
optionFile.close();
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_H
typedef long long int64;
typedef unsigned long long uint64;
-static const int64 COIN = 100000000;
-static const int64 CENT = 1000000;
+static const int64 COIN = 1000000;
+static const int64 CENT = 10000;
#define loop for (;;)
#define BEGIN(a) ((char*)&(a))
}
#endif
-
-
+#ifndef THROW_WITH_STACKTRACE
+#define THROW_WITH_STACKTRACE(exception) \
+{ \
+ LogStackTrace(); \
+ throw (exception); \
+}
+void LogStackTrace();
+#endif
// Note: It turns out we might have been able to use boost::thread
// by using TerminateThread(boost::thread.native_handle(), 0);
#ifdef WIN32
-typedef HANDLE pthread_t;
+typedef HANDLE bitcoin_pthread_t;
-inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false)
+inline bitcoin_pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false)
{
DWORD nUnused = 0;
HANDLE hthread =
if (hthread == NULL)
{
printf("Error: CreateThread() returned %d\n", GetLastError());
- return (pthread_t)0;
+ return (bitcoin_pthread_t)0;
}
if (!fWantHandle)
{
CloseHandle(hthread);
- return (pthread_t)-1;
+ return (bitcoin_pthread_t)-1;
}
return hthread;
}
#ifndef BUILD_DESC
# ifdef GIT_COMMIT_ID
-# define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID)
+# define BUILD_DESC BUILD_DESC_FROM_COMMIT(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD, GIT_COMMIT_ID)
# else
-# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD)
+# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(PPCOIN_VERSION_MAJOR, PPCOIN_VERSION_MINOR, PPCOIN_VERSION_REVISION, PPCOIN_VERSION_BUILD)
# endif
#endif
extern const std::string CLIENT_BUILD;
extern const std::string CLIENT_DATE;
+// ppcoin version - intended for display purpose ONLY
+#define PPCOIN_VERSION_MAJOR 0
+#define PPCOIN_VERSION_MINOR 1
+#define PPCOIN_VERSION_REVISION 0
+#define PPCOIN_VERSION_BUILD 0
+
//
// network protocol versioning
//
static const int PROTOCOL_VERSION = 60001;
// earlier versions not supported as of Feb 2012, and are disconnected
-static const int MIN_PROTO_VERSION = 209;
+static const int MIN_PROTO_VERSION = 60001;
// nTime field added to CAddress, starting with this version;
// if possible, avoid requesting addresses nodes older than this
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet.h"
#include "walletdb.h"
#include "crypter.h"
+#include "checkpoints.h"
#include "ui_interface.h"
using namespace std;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
+// ppcoin: optional setting to create coinstake only when unlocked;
+// serves to disable the trivial sendmoney when OS account compromised
+bool fWalletUnlockStakeOnly = false;
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
if (!IsLocked())
CWalletTx& wtx = (*mi).second;
if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{
- printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
vWalletUpdated.push_back(txin.prevout.hash);
int nRequests = -1;
{
LOCK(pwallet->cs_wallet);
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
// Generated block
if (hashBlock != 0)
listSent.clear();
strSentAccount = strFromAccount;
- if (IsCoinBase())
+ if (IsCoinBase() || IsCoinStake())
{
if (GetBlocksToMaturity() > 0)
nGeneratedImmature = pwallet->GetCredit(*this);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
- if (wtx.IsCoinBase() && wtx.IsSpent(0))
+ if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
CTxIndex txindex;
}
if (fUpdated)
{
- printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
+ printf("ReacceptWalletTransactions found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk();
}
else
{
// Reaccept any txes of ours that aren't already in a block
- if (!wtx.IsCoinBase())
+ if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
wtx.AcceptWalletTransaction(txdb, false);
}
}
{
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{
- if (!tx.IsCoinBase())
+ if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();
if (!txdb.ContainsTx(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
}
}
- if (!IsCoinBase())
+ if (!(IsCoinBase() || IsCoinStake()))
{
uint256 hash = GetHash();
if (!txdb.ContainsTx(hash))
return nTotal;
}
-bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+// ppcoin: total coins staked (non-spendable until maturity)
+int64 CWallet::GetStake() const
+{
+ int64 nTotal = 0;
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+ nTotal += CWallet::GetCredit(*pcoin);
+ }
+ return nTotal;
+}
+
+int64 CWallet::GetNewMint() const
+{
+ int64 nTotal = 0;
+ LOCK(cs_wallet);
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx* pcoin = &(*it).second;
+ if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
+ nTotal += CWallet::GetCredit(*pcoin);
+ }
+ return nTotal;
+}
+
+
+bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
setCoinsRet.clear();
nValueRet = 0;
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
+ if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
continue;
+ if (pcoin->nTime > nSpendTime)
+ continue; // ppcoin: timestamp must not exceed spend time
+
int64 n = pcoin->vout[i].nValue;
if (n <= 0)
return true;
}
-bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
- return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
+ return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet));
}
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
- if (!SelectCoins(nTotalValue, setCoins, nValueIn))
+ if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
// Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
- bool fAllowFree = CTransaction::AllowFree(dPriority);
- int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND);
+ int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee))
{
nFeeRet = max(nPayFee, nMinFee);
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
}
+// ppcoin: create coin stake transaction
+bool CWallet::CreateCoinStake(unsigned int nBits, CTransaction& txNew)
+{
+ CBigNum bnTargetPerCoinDay;
+ bnTargetPerCoinDay.SetCompact(nBits);
+
+ LOCK2(cs_main, cs_wallet);
+ txNew.vin.clear();
+ txNew.vout.clear();
+ // Mark coin stake transaction
+ CScript scriptEmpty;
+ scriptEmpty.clear();
+ txNew.vout.push_back(CTxOut(0, scriptEmpty));
+ // Choose coins to use
+ int64 nBalance = GetBalance();
+ if (nBalance <= nBalanceReserve)
+ return false;
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
+ vector<const CWalletTx*> vwtxPrev;
+ int64 nValueIn = 0;
+ if (!SelectCoins(nBalance - nBalanceReserve, txNew.nTime, setCoins, nValueIn))
+ return false;
+ if (setCoins.empty())
+ return false;
+ int64 nCredit = 0;
+ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ {
+ CTxDB txdb("r");
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex))
+ continue;
+
+ // Read block header
+ CBlock block;
+ if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
+ continue;
+ if (block.GetBlockTime() + STAKE_MIN_AGE > txNew.nTime)
+ continue; // only count coins meeting min age requirement
+
+ int64 nValueIn = pcoin.first->vout[pcoin.second].nValue;
+ CBigNum bnCoinDay = CBigNum(nValueIn) * (txNew.nTime-pcoin.first->nTime) / COIN / (24 * 60 * 60);
+ // Calculate hash
+ CDataStream ss(SER_GETHASH, 0);
+ ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << pcoin.first->nTime << pcoin.second << txNew.nTime;
+ if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
+ {
+ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+ nCredit += pcoin.first->vout[pcoin.second].nValue;
+ vwtxPrev.push_back(pcoin.first);
+ // Set output scriptPubKey
+ txNew.vout.push_back(CTxOut(0, pcoin.first->vout[pcoin.second].scriptPubKey));
+ break;
+ }
+ }
+ if (nCredit == 0 || nCredit > nBalance - nBalanceReserve)
+ return false;
+ BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ {
+ if (pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey && pcoin.first->GetHash() != txNew.vin[0].prevout.hash)
+ {
+ if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nBalanceReserve)
+ break;
+ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+ nCredit += pcoin.first->vout[pcoin.second].nValue;
+ vwtxPrev.push_back(pcoin.first);
+ }
+ }
+ // Calculate coin age reward
+ {
+ uint64 nCoinAge;
+ CTxDB txdb("r");
+ if (!txNew.GetCoinAge(txdb, nCoinAge))
+ return error("CreateCoinStake : failed to calculate coin age");
+ nCredit += GetProofOfStakeReward(nCoinAge);
+ }
+ // Set output amount
+ txNew.vout[1].nValue = nCredit;
+
+ // Sign
+ int nIn = 0;
+ BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev)
+ {
+ if (!SignSignature(*this, *pcoin, txNew, nIn++))
+ return error("CreateCoinStake : failed to sign coinstake");
+ }
+ return true;
+}
+
// Call after CreateTransaction unless you want to abort
bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
{
printf("SendMoney() : %s", strError.c_str());
return strError;
}
+ if (fWalletUnlockStakeOnly)
+ {
+ string strError = _("Error: Wallet unlocked for coinstake only, unable to create transaction.");
+ printf("SendMoney() : %s", strError.c_str());
+ return strError;
+ }
if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired))
{
string strError;
{
{
LOCK(cs_wallet);
- if (mapWallet.count(block.vtx[0].GetHash()))
+ if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash()))
{
CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()];
- printf(" mine: %d %d %d", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit());
+ printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
+ }
+ if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash()))
+ {
+ CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()];
+ printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str());
}
}
printf("\n");
return keypool.nTime;
}
+// ppcoin: check 'spent' consistency between wallet and txindex
+bool CWallet::CheckSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+
+ LOCK(cs_wallet);
+ vector<const CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ vCoins.push_back(&(*it).second);
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
+ {
+ // Find the corresponding transaction index
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+ continue;
+ for (int n=0; n < pcoin->vout.size(); n++)
+ {
+ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ {
+ printf("CheckSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ {
+ printf("CheckSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ }
+ }
+ }
+ return (nMismatchFound == 0);
+}
+
+// ppcoin: fix wallet spent state according to txindex
+void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion)
+{
+ nMismatchFound = 0;
+ nBalanceInQuestion = 0;
+
+ LOCK(cs_wallet);
+ vector<CWalletTx*> vCoins;
+ vCoins.reserve(mapWallet.size());
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ vCoins.push_back(&(*it).second);
+
+ CTxDB txdb("r");
+ BOOST_FOREACH(CWalletTx* pcoin, vCoins)
+ {
+ // Find the corresponding transaction index
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex))
+ continue;
+ for (int n=0; n < pcoin->vout.size(); n++)
+ {
+ if (pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull()))
+ {
+ printf("FixSpentCoins found lost coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ pcoin->MarkUnspent(n);
+ pcoin->WriteToDisk();
+ }
+ else if (!pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull()))
+ {
+ printf("FixSpentCoins found spent coin %sppc %s[%d]\n", FormatMoney(pcoin->GetCredit()).c_str(), pcoin->GetHash().ToString().c_str(), n);
+ nMismatchFound++;
+ nBalanceInQuestion += pcoin->vout[n].nValue;
+ pcoin->MarkSpent(n);
+ pcoin->WriteToDisk();
+ }
+ }
+ }
+}
+
+// ppcoin: disable transaction (only for coinstake)
+void CWallet::DisableTransaction(const CTransaction &tx)
+{
+ if (!tx.IsCoinStake() || !IsFromMe(tx))
+ return; // only disconnecting coinstake requires marking input unspent
+
+ LOCK(cs_wallet);
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
+ if (mi != mapWallet.end())
+ {
+ CWalletTx& prev = (*mi).second;
+ if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n]))
+ {
+ prev.MarkUnspent(txin.prevout.n);
+ prev.WriteToDisk();
+ }
+ }
+ }
+}
+
vector<unsigned char> CReserveKey::GetReservedKey()
{
if (nIndex == -1)
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2011-2012 The PPCoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_H
#include "keystore.h"
#include "script.h"
+extern bool fWalletUnlockStakeOnly;
+
class CWalletTx;
class CReserveKey;
class CWalletDB;
class CWallet : public CCryptoKeyStore
{
private:
- bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
- bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
+ bool SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
+ bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
CWalletDB *pwalletdbEncryption;
void ResendWalletTransactions();
int64 GetBalance() const;
int64 GetUnconfirmedBalance() const;
+ int64 GetStake() const;
+ int64 GetNewMint() const;
bool CreateTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
+ bool CreateCoinStake(unsigned int nBits, CTransaction& txNew);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
std::string SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
int GetVersion() { return nWalletVersion; }
+
+ bool CheckSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion);
+ void FixSpentCoins(int& nMismatchSpent, int64& nBalanceInQuestion);
+ void DisableTransaction(const CTransaction &tx);
};
/** A key allocated from the key pool. */
}
}
+ void MarkUnspent(unsigned int nOut)
+ {
+ if (nOut >= vout.size())
+ throw std::runtime_error("CWalletTx::MarkUnspent() : nOut out of range");
+ vfSpent.resize(vout.size());
+ if (vfSpent[nOut])
+ {
+ vfSpent[nOut] = false;
+ fAvailableCreditCached = false;
+ }
+ }
+
bool IsSpent(unsigned int nOut) const
{
if (nOut >= vout.size())
int64 GetCredit(bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsCoinBase() && GetBlocksToMaturity() > 0)
+ if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
// GetBalance can assume transactions in mapWallet won't change
int64 GetAvailableCredit(bool fUseCache=true) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
- if (IsCoinBase() && GetBlocksToMaturity() > 0)
+ if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0)
return 0;
if (fUseCache && fAvailableCreditCached)