def make_snapshot_from_ops_hash( cls, record_root_hash, prev_consensus_hashes ):
        """
        Generate the consensus hash from the hash over the current ops, and
        all previous required consensus hashes.
        """

        # mix into previous consensus hashes...
        all_hashes = prev_consensus_hashes[:] + [record_root_hash]
        all_hashes.sort()
        all_hashes_merkle_tree = pybitcoin.MerkleTree( all_hashes )
        root_hash = all_hashes_merkle_tree.root()

        consensus_hash = StateEngine.calculate_consensus_hash( root_hash )
        return consensus_hash
def block_verify(block_data):
    """
    Given block data (a dict with 'merkleroot' hex string and 'tx' list of hex strings--i.e.
    a block returned from bitcoind's getblock JSON RPC method), verify that the
    transactions are consistent.

    Return True on success
    Return False if not.
    """

    # verify block data txs
    m = pybitcoin.MerkleTree(block_data['tx'])
    root_hash = str(m.root())

    return root_hash == str(block_data['merkleroot'])
    def make_ops_snapshot(cls, serialized_ops):
        """
        Generate a deterministic hash over the sequence of (serialized) operations.
        """
        record_hashes = []
        for serialized_op in serialized_ops:
            record_hash = binascii.hexlify(pybitcoin.hash.bin_double_sha256(serialized_op))
            record_hashes.append(record_hash)

        if len(record_hashes) == 0:
            record_hashes.append(binascii.hexlify(pybitcoin.hash.bin_double_sha256("")))

        # put records into their own Merkle tree, and mix the root with the consensus hashes.
        record_hashes.sort()
        record_merkle_tree = pybitcoin.MerkleTree(record_hashes)
        record_root_hash = record_merkle_tree.root()

        return record_root_hash
示例#4
0
    def handle_block(self, message_header, block):
        """
        Got a block.
        * validate it
        * load its transactions
        * ask for each transaction's sender transaction
        """

        if self.have_all_block_data():
            self.loop_exit()
            return

        block_hash = block.calculate_hash()

        # is this a solicited block?
        if block_hash not in self.block_info.keys():
            log.error("Ignoring unsolicited block %s" % block_hash)
            return

        header = self.block_info[block_hash]['header']
        height = self.block_info[block_hash]['height']

        log.debug("handle block %s (%s)" % (height, block_hash))

        # does this block's transaction hashes match the merkle root?
        tx_hashes = [
            block.txns[i].calculate_hash() for i in xrange(0, len(block.txns))
        ]
        mr = pybitcoin.MerkleTree(tx_hashes).root()

        if mr != header['merkle_root']:
            log.error("Merkle root of %s (%s) mismatch: expected %s, got %s" %
                      (block_hash, height, header['merkle_root'], mr))
            return

        nulldata_txs = []
        relindex = 0
        for txindex in xrange(0, len(block.txns)):

            txdata = self.parse_tx(block.txns[txindex], header, block_hash,
                                   txindex)

            # if there is no nulldata output, then we don't care about this one.
            has_nulldata = False
            nulldata_payload = None
            for outp in txdata['vout']:
                if outp['scriptPubKey']['type'] == 'nulldata':
                    has_nulldata = True
                    nulldata_payload = bitcoin.deserialize_script(
                        outp['scriptPubKey']['hex'])[1]
                    if type(nulldata_payload) not in [str, unicode]:
                        # this is a malformed OP_RETURN, where the varint that should follow OP_RETURN doesn't have the data behind it.
                        # just take the data after the varint, no matter what it is (i.e. "6a52" will be "")
                        nulldata_payload = outp['scriptPubKey']['hex'][4:]

            # count all txs processed
            self.num_txs_processed += 1

            if not has_nulldata:
                continue

            # remember nulldata
            txdata['nulldata'] = nulldata_payload

            # calculate total output (part of fee; will be debited when we discover the senders)
            txdata['fee'] -= sum(
                int(out['value'] * 10**8) for out in txdata['vout'])

            # remember the relative tx index (i.e. the ith nulldata tx)
            txdata['relindex'] = relindex

            # do we actually want this?
            if self.tx_filter is not None:
                if not self.tx_filter(txdata):
                    continue

            # yup, we want it!
            relindex += 1
            nulldata_txs.append(txdata)

        self.block_info[block_hash]['txns'] = nulldata_txs
        self.block_info[block_hash]['num_txns'] = len(block.txns)
        self.block_info[block_hash]['num_senders'] = 0

        # get each input's transaction
        sender_txhashes = []

        for txn in self.block_info[block_hash]['txns']:
            for i in xrange(0, len(txn['vin'])):

                # record information about the transaction
                # that created this input (so we can go find
                # it later).
                inp = txn['vin'][i]
                sender_txid = inp['txid']
                inp_sender_outp = inp['vout']

                if str(sender_txid) not in sender_txhashes:
                    sender_txhashes.append(str(sender_txid))

                sinfo = self.make_sender_info(block_hash, txn, i)

                if not self.sender_info.has_key(sender_txid):
                    # map outpoint for this input to the tx info
                    self.sender_info[sender_txid] = {}

                # sinfo is the information from the output in
                # the sender-tx that funded inp
                self.sender_info[sender_txid][inp_sender_outp] = sinfo

        # update accounting...
        self.num_blocks_received += 1
        self.block_info[block_hash]['handled'] = True

        log.debug("Request %s nulldata sender TXs" % len(sender_txhashes))

        if self.have_all_block_data():
            self.loop_exit()

        return
示例#5
0
    def flush_transactions(self):
        """
        TESTING ONLY

        Send the bufferred list of transactions as a block.
        """

        # next block
        txs = self.next_block_txs
        self.next_block_txs = []

        # add a fake coinbase
        txs.append(
            "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff53038349040d00456c69676975730052d8f72ffabe6d6dd991088decd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b0400000000000000002f73733331312f00906b570400000000e4050000ffffffff01bf208795000000001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000"
        )

        block_txs = {}
        block_txids = []
        for tx in txs:
            txid = self.make_txid(tx)
            block_txids.append(txid)
            block_txs[txid] = tx

        version = '01000000'
        t_hex = "%08X" % self.time
        difficulty_hex = "%08X" % self.difficulty
        tx_merkle_tree = pybitcoin.MerkleTree(block_txs)
        tx_merkle_root = binascii.hexlify(tx_merkle_tree.root())
        prev_block_hash = self.block_hashes[self.end_block - 1]

        block_header = version + prev_block_hash + tx_merkle_root + t_hex + difficulty_hex + '00000000'

        # NOTE: not accurate; just get *a* hash
        block_hash = self.make_txid(block_header)

        # update nextblockhash at least
        self.blocks[prev_block_hash]['nextblockhash'] = block_hash

        # next block
        block = {
            'merkleroot': tx_merkle_root,
            'nonce': 0,  # mock
            'previousblockhash': prev_block_hash,
            'hash': block_hash,
            'version': 3,
            'tx': block_txids,
            'chainwork': '00' * 32,  # mock
            'height': self.end_block,
            'difficulty': Decimal(0.0),  # mock
            'nextblockhash': None,  # to be filled in
            'confirmations': None,  # to be filled in 
            'time': self.time,  # mock
            'bits': 0x00000000,  # mock
            'size': sum([len(tx) for tx in txs]) + len(block_hash)  # mock
        }

        self.block_hashes[self.end_block] = block_hash
        self.blocks[block_hash] = block
        self.txs.update(block_txs)

        self.time += 600  # 10 minutes
        self.difficulty += 1
        self.end_block += 1

        return [self.make_txid(tx) for tx in txs]
示例#6
0
    def handle_block(self, message_header, block):
        log.info("handle block message %s" % block)
        if self.have_all_block_data():
            self.loop_exit()
            return

        block_hash = block.calculate_hash()

        # Is it a solicited block
        if block_hash not in self.block_info.keys():
            log.error("Ignoring unsolicited block %s" % (block_hash))
            return

        header = self.block_info[block_hash]['header']
        height = self.block_info[block_hash]['height']

        # does the block's transaction hashes match the merkle root
        tx_hashes = [
            block.txns[i].calculate_hash() for i in xrange(0, len(block.txns))
        ]
        mr = pybitcoin.MerkleTree(tx_hashes).root()

        if mr != header['merkle_root']:
            log.error("Merkle root %s (%s) mismatch: expected %s, got %s" %
                      (block_hash, height, header['merkle_root'], mr))
            return

        nulldata_txs = []
        relindex = 0

        log.info("The origin block is %s" % block)

        log.info("The initial txns format is %s " % block.txns)

        for txindex in xrange(0, len(block.txns)):

            txdata = self.parse_tx(block.txns[txindex], header, block_hash,
                                   txindex)
            has_nulldata = False
            nulldata_payload = None

            for outp in txdata['vout']:
                if outp['scriptPubkey']['type'] == 'nulldata':
                    has_nulldata = True
                    nulldata_payload = bitcoin.deserialize_script(
                        outp['scriptPubkey']['hex'])[1]
                    if type(nulldata_payload) not in [str, unicode]:
                        log.debug("Malformed nulldata format")
                        nulldata_payload = outp['scriptPubkey']['hex'][4:]

            if not has_nulldata:
                continue
            log.info('nulldata is %s ' % binascii.unhexlify(nulldata_payload))
            txdata['nulldata'] = nulldata_payload
            txdata['relindex'] = relindex

            relindex += 1
            nulldata_txs.append(txdata)

        self.block_info[block_hash]['txns'] = nulldata_txs
        self.block_info[block_hash]['num_txns'] = len(block.txns)
        self.block_info[block_hash]['num_senders'] = 0

        sender_txhashes = []
        for txn in self.block_info[block_hash]['txns']:
            for i in xrange(0, len(txn['vin'])):
                inp = txn['vin'][i]
                sender_txid = inp['txid']
                inp_sender_outp = inp['vout']

                if str(sender_txid) not in sender_txhashes:
                    sender_txhashes.append(str(sender_txid))

                sinfo = self.make_sender_info(block_hash, txn, i)

                if not self.sender_info.has_key(sender_txid):
                    self.sender_info[sender_txid] = {}

                self.sender_info[sender_txid][inp_sender_outp] = sinfo

        # update accounting
        self.num_blocks_received += 1
        self.block_info[block_hash]['handled'] = True

        log.debug("Request %s nulldata sender TXs" % len(sender_txhashes))

        if self.have_all_block_data():
            self.loop_exit()

        return
    def flush_transactions( self ):
        """
        TESTING ONLY

        Send the bufferred list of transactions as a block.
        Save the resulting transactions to a temporary file.
        """
        
        # next block
        txs = []
        if self.next_block_txs_path is not None and os.path.exists( self.next_block_txs_path ):
            txs = self.restore_next( self.next_block_txs_path )
            os.unlink( self.next_block_txs_path )

        # add a fake coinbase 
        txs.append( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff53038349040d00456c69676975730052d8f72ffabe6d6dd991088decd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b0400000000000000002f73733331312f00906b570400000000e4050000ffffffff01bf208795000000001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000" )

        block_txs = {}
        block_txids = []
        for tx in txs:
            txid = make_txid( str(tx) )
            block_txids.append( txid )
            block_txs[ txid ] = tx

        version = '01000000'
        t_hex = "%08X" % self.time
        difficulty_hex = "%08X" % self.difficulty
        tx_merkle_tree = pybitcoin.MerkleTree( block_txids )
        tx_merkle_root = tx_merkle_tree.root()
        prev_block_hash = self.block_hashes[ self.end_block - 1 ]        

        # next block
        block = {
            'merkleroot': tx_merkle_root,
            'nonce': 0,                 # mock
            'previousblockhash': prev_block_hash,
            'version': 3,
            'tx': block_txids,
            'chainwork': '00' * 32,     # mock
            'height': self.end_block,
            'difficulty': Decimal(0.0), # mock
            'nextblockhash': None,      # to be filled in
            'confirmations': None,      # to be filled in 
            'time': self.time,          # mock
            'bits': "0x00000000",       # mock
            'size': sum( [len(tx) for tx in txs] ) + 32   # mock
        }

        block_header = bitcoin.main.encode(block['version'], 256, 4)[::-1] + \
                       block['previousblockhash'].decode('hex')[::-1] + \
                       block['merkleroot'].decode('hex')[::-1] + \
                       bitcoin.main.encode(block['time'], 256, 4)[::-1] + \
                       bitcoin.main.encode(int(block['bits'], 16), 256, 4)[::-1] + \
                       bitcoin.main.encode(block['nonce'], 256, 4)[::-1] 

        block['hash'] = pybitcoin.bin_double_sha256( block_header )[::-1].encode('hex')
        block['header'] = block_header

        for txid in block['tx']:
            # update txid --> blockhash map 
            self.txid_to_blockhash[ txid ] = block['hash']

        # update nextblockhash at least 
        self.blocks[prev_block_hash]['nextblockhash'] = block['hash']
        self.block_hashes[ self.end_block ] = block['hash']
        self.blocks[ block['hash'] ] = block
        self.txs.update( block_txs )

        self.time += 600    # 10 minutes
        self.difficulty += 1
        self.end_block += 1

        if self.save_file is not None:
            self.save( self.save_file )

        if self.spv_headers_path is not None:
            with open(self.spv_headers_path, "a+") as f:
                f.write( block_header )
                f.write( "00".decode('hex') )    # our SPV client expects varint for tx count to be zero

        return [ make_txid( str(tx) ) for tx in txs ]