From d0439647dfc60efce32e0a13b0ec02428ba412cf Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Sun, 20 Jul 2014 01:55:34 +0400 Subject: [PATCH] Novacoin: beginning of adaptation for NVC protocol. * Set new genesis block hash and address version values; * Use getblockbynumber RPC instead of getblockhash & getblock & getrawtransaction; * Handle proof-of-stake blocks and coinstake transactions correctly. --- backends/bitcoind/blockchain_processor.py | 70 ++++++++-------------------- backends/bitcoind/deserialize.py | 12 +++-- backends/bitcoind/storage.py | 2 +- server.py | 2 +- start | 22 +++++----- utils/__init__.py | 2 +- 6 files changed, 41 insertions(+), 69 deletions(-) diff --git a/backends/bitcoind/blockchain_processor.py b/backends/bitcoind/blockchain_processor.py index 38577c6..644e74e 100644 --- a/backends/bitcoind/blockchain_processor.py +++ b/backends/bitcoind/blockchain_processor.py @@ -9,7 +9,7 @@ import time import threading import traceback import urllib - +from ltc_scrypt import getPoWHash from backends.bitcoind import deserialize from processor import Processor, print_log from utils import * @@ -135,8 +135,7 @@ class BlockchainProcessor(Processor): } def get_header(self, height): - block_hash = self.bitcoind('getblockhash', [height]) - b = self.bitcoind('getblock', [block_hash]) + b = self.bitcoind('getblockbynumber', [height]) return self.block2header(b) def init_headers(self, db_height): @@ -174,7 +173,7 @@ class BlockchainProcessor(Processor): self.flush_headers() def hash_header(self, header): - return rev_hex(Hash(header_to_string(header).decode('hex')).encode('hex')) + return rev_hex(getPoWHash(header_to_string(header).decode('hex')).encode('hex')) def read_header(self, block_height): if os.path.exists(self.headers_filename): @@ -236,7 +235,7 @@ class BlockchainProcessor(Processor): vds = deserialize.BCDataStream() vds.write(raw_tx.decode('hex')) try: - return deserialize.parse_Transaction(vds, is_coinbase=False) + return deserialize.parse_Transaction(vds, is_coinbase=False, is_coinstake=False) except: print_log("ERROR: cannot parse", txid) return None @@ -302,8 +301,7 @@ class BlockchainProcessor(Processor): def get_merkle(self, tx_hash, height): - block_hash = self.bitcoind('getblockhash', [height]) - b = self.bitcoind('getblock', [block_hash]) + b = self.bitcoind('getblockbynumber', [height]) tx_list = b.get('tx') tx_pos = tx_list.index(tx_hash) @@ -358,22 +356,27 @@ class BlockchainProcessor(Processor): def deserialize_block(self, block): + is_stake_block = False txlist = block.get('tx') + if "proof-of-stake" in block.get('flags'): # scan block flags list for + is_stake_block = True # "proof-of-stake" substring + tx_hashes = [] # ordered txids txdict = {} # deserialized tx - is_coinbase = True - for raw_tx in txlist: - tx_hash = hash_encode(Hash(raw_tx.decode('hex'))) + + for i in xrange(len(txlist)): + if is_stake_block and i == 0: # skip coinbase for + continue # stake block + tx_hash = hash_encode(Hash(txlist[i].decode('hex'))) vds = deserialize.BCDataStream() - vds.write(raw_tx.decode('hex')) + vds.write(txlist[i].decode('hex')) try: - tx = deserialize.parse_Transaction(vds, is_coinbase) - except: + tx = deserialize.parse_Transaction(vds, i == 0, is_stake_block and i == 1) # first transaction is always coinbase + except: # second transaction is coinstake if we have a stake block print_log("ERROR: cannot parse", tx_hash) continue tx_hashes.append(tx_hash) txdict[tx_hash] = tx - is_coinbase = False return tx_hashes, txdict @@ -600,39 +603,6 @@ class BlockchainProcessor(Processor): elif result != '': self.push_response(session, {'id': message_id, 'result': result}) - - def getfullblock(self, block_hash): - block = self.bitcoind('getblock', [block_hash]) - - rawtxreq = [] - i = 0 - for txid in block['tx']: - rawtxreq.append({ - "method": "getrawtransaction", - "params": [txid], - "id": i, - }) - i += 1 - - postdata = dumps(rawtxreq) - try: - respdata = urllib.urlopen(self.bitcoind_url, postdata).read() - except: - print_log("bitcoind error (getfullblock)") - traceback.print_exc(file=sys.stdout) - self.shared.stop() - - r = loads(respdata) - rawtxdata = [] - for ir in r: - if ir['error'] is not None: - self.shared.stop() - print_log("Error: make sure you run bitcoind with txindex=1; use -reindex if needed.") - raise BaseException(ir['error']) - rawtxdata.append(ir['result']) - block['tx'] = rawtxdata - return block - def catch_up(self, sync=True): prev_root_hash = None @@ -650,8 +620,8 @@ class BlockchainProcessor(Processor): # not done.. self.up_to_date = False - next_block_hash = self.bitcoind('getblockhash', [self.storage.height + 1]) - next_block = self.getfullblock(next_block_hash) + next_block = self.bitcoind('getblockbynumber', [self.storage.height + 1, True]) + next_block_hash = next_block.get('hash') self.mtime('daemon') # fixme: this is unsafe, if we revert when the undo info is not yet written @@ -677,7 +647,7 @@ class BlockchainProcessor(Processor): else: # revert current block - block = self.getfullblock(self.storage.last_hash) + block = self.bitcoind('getblock', [self.storage.last_hash, True]) print_log("blockchain reorg", self.storage.height, block.get('previousblockhash'), self.storage.last_hash) self.import_block(block, self.storage.last_hash, self.storage.height, sync, revert=True) self.pop_header() diff --git a/backends/bitcoind/deserialize.py b/backends/bitcoind/deserialize.py index 93ad4d4..7effb18 100644 --- a/backends/bitcoind/deserialize.py +++ b/backends/bitcoind/deserialize.py @@ -239,10 +239,11 @@ def parse_TxOut(vds, i): return d -def parse_Transaction(vds, is_coinbase): +def parse_Transaction(vds, is_coinbase, is_coinstake): d = {} start = vds.read_cursor d['version'] = vds.read_int32() + d['timestamp'] = vds.read_int32() n_vin = vds.read_compact_size() d['inputs'] = [] for i in xrange(n_vin): @@ -258,7 +259,8 @@ def parse_Transaction(vds, is_coinbase): # print("skipping strange tx output with zero value") # continue # if o['address'] != "None": - d['outputs'].append(o) + if not is_coinstake or i > 0: # first coinstake output + d['outputs'].append(o) # transaction doesn't make any sense d['lockTime'] = vds.read_uint32() return d @@ -376,13 +378,13 @@ def get_address_from_input_script(bytes): match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ] if match_decoded(dec2, match2): pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex') ] - return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5) + return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 20) # 2 of 3 match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ] if match_decoded(dec2, match2): pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex'), dec2[3][1].encode('hex') ] - return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 5) + return pubkeys, signatures, hash_160_to_bc_address(hash_160(redeemScript), 20) return [], [], None @@ -419,7 +421,7 @@ def get_address_from_output_script(bytes): # p2sh match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ] if match_decoded(decoded, match): - addr = hash_160_to_bc_address(decoded[1][1],5) + addr = hash_160_to_bc_address(decoded[1][1],20) return addr return None diff --git a/backends/bitcoind/storage.py b/backends/bitcoind/storage.py index cd742fd..098d12e 100644 --- a/backends/bitcoind/storage.py +++ b/backends/bitcoind/storage.py @@ -42,7 +42,7 @@ class Storage(object): #traceback.print_exc(file=sys.stdout) print_log('initializing database') self.height = 0 - self.last_hash = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' + self.last_hash = '00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4' db_version = self.db_version # write root self.put_node('', {}) diff --git a/server.py b/server.py index 3a6f246..4cc6832 100755 --- a/server.py +++ b/server.py @@ -60,7 +60,7 @@ def create_config(): config.set('server', 'password', '') config.set('server', 'irc', 'no') config.set('server', 'irc_nick', '') - config.set('server', 'coin', '') + config.set('server', 'novacoin', '') config.set('server', 'datadir', '') # use leveldb as default diff --git a/start b/start index e0d0bc9..35624d1 100755 --- a/start +++ b/start @@ -7,7 +7,7 @@ if [[ $PID != *[!0-9]* ]]; then fi -electrum_config="/etc/electrum.conf" +electrum_config="electrum.conf" if [ ! -f $electrum_config ]; then echo "$electrum_config does not exist" @@ -24,15 +24,15 @@ fi rmdir $path --ignore-fail-on-non-empty -if [ ! -d $path ]; then - echo "Database not found in $path." - read -p "Do you want to download it from the Electrum foundry to $path ? " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]]; then - mkdir -p $path - wget -O - "http://foundry.electrum.org/leveldb-dump/electrum-fulltree-100-latest.tar.gz" | tar --extract --gunzip --strip-components 1 --directory $path --file - - fi -fi +#if [ ! -d $path ]; then +# echo "Database not found in $path." +# read -p "Do you want to download it from the Electrum foundry to $path ? " -n 1 -r +# echo +# if [[ $REPLY =~ ^[Yy]$ ]]; then +# mkdir -p $path +# wget -O - "http://foundry.electrum.org/leveldb-dump/electrum-fulltree-100-latest.tar.gz" | tar --extract --gunzip --strip-components 1 --directory $path --file - +# fi +#fi echo "Starting server as daemon" -nohup /usr/bin/python -u server.py &> /var/log/electrum.log & +nohup /usr/bin/python -u server.py &> electrum.log & diff --git a/utils/__init__.py b/utils/__init__.py index d6ff895..8a6cad2 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -107,7 +107,7 @@ def public_key_to_bc_address(public_key): return hash_160_to_bc_address(hash_160(public_key)) -def hash_160_to_bc_address(h160, addrtype = 0): +def hash_160_to_bc_address(h160, addrtype = 8): if h160 == 'None': return 'None' vh160 = chr(addrtype) + h160 -- 1.7.1