Example #1
0
 def __init__(self, node, blockchain, txpool, process_pool, log):
     Observable.__init__(self)
     self.node = node
     self.blockchain = blockchain
     self.txpool = txpool
     self.log = log
     self.items_to_download = deque()
     self.requested_tx = {}
     self.orphan_tx = {}
     self.process_pool = process_pool
     self.txverifier = TxVerifier(self.blockchain.database.runmode)
     
     node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_INV), self.on_inv)
     node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_TX), self.on_tx)
     
     self.node.subscribe (Node.EVT_DISCONNECTED, self.on_peer_disconnected)
Example #2
0
 def __init__(self, runmode):
     self.runmode = runmode
     self.block_serializer = BlockSerializer()
     self.tx_verifier = TxVerifier(self.runmode)
Example #3
0
class BlockVerifier():
    def __init__(self, runmode):
        self.runmode = runmode
        self.block_serializer = BlockSerializer()
        self.tx_verifier = TxVerifier(self.runmode)
        
    """
        basic_check: run tests that don't require context or the block parent.
    """
    def basic_checks(self, hash, block):
        self.check_size_limit(hash, block)
        self.check_proof_of_work(hash, block)
        self.check_coinbase(hash, block)
        self.check_transactions(hash, block)
        self.check_merkle_root(hash, block)
                                    

    def check_size_limit(self, hash, block):
        #FIXME: don't serialize the block here (add get_serialize_size() in serialize objects
        #or cache previoulsy serialized block)
        if (not block.rawdata):
            block.rawdata = self.block_serializer.serialize(block)
        if len(block.rawdata) > MAX_BLOCK_SIZE:
            raise Exception("block size limit exceeded")
    
    def check_proof_of_work(self, hash, block):
        target = block.blockheader.target()
        if (target <= Uint256.zero() or target > PROOF_OF_WORK_LIMIT[self.runmode]):
            raise Exception("proof of work: value out of range : %x" % (block.blockheader.bits))
        if (hash > target):
            raise Exception("proof of work: hash doesn't match target hash:%s, target:%s" % (hash, target))
    
    # not out of context: (check elsewhere) 
    # def check_timestamp(self, hash, block):
    #     block.blockheader.time > time.time
    


    def check_coinbase(self, hash, block):
        if not len(block.transactions):
            raise Exception("Block has no transactions" )
        if not block.transactions[0].iscoinbase():
            raise Exception("block's first transactions is not coinbase" )
        for tx in block.transactions[1:]:
            if tx.iscoinbase():
                raise Exception("more than one coinbase" )
    
    def check_transactions(self, hash, block):
        for tx in block.transactions:
            err = self.tx_verifier.basic_checks(tx)
            if err:
                return err

    def check_merkle_root(self, hash, block):
        merkle = compute_merkle_root(block.transactions)
        if merkle != block.blockheader.hash_merkle:
            raise Exception("merkel root incorrect for block %s: %s != %s" % (str(hash), str(merkle), str(block.blockheader.hash_merkle)) )
            
    """
        accept_block: check block after finding the parent block.
    """
    #AcceptBlock: main.cpp:1445
    def accept_block(self, hash, block, blockchain):
        prevblockhandle = blockchain.get_block_handle(block.blockheader.hash_prev)
        
        self.check_target(blockchain, prevblockhandle, hash, block)
        self.check_timestamp(blockchain, prevblockhandle, hash, block)
        self.check_tx_finalized(prevblockhandle, hash, block)
        self.check_checkpoints(prevblockhandle, hash, block)
      
        
    def check_target(self, blockchain, prevblockhandle, hash, block):
        #Check proof of work target
        if (blockchain.get_next_work_required(prevblockhandle.hash, block) != block.blockheader.bits):
            raise Exception("Incorrect difficulty target, found:%08x != required:%08x in block %s" % (block.blockheader.bits, blockchain.get_next_work_required(prevblockhandle.hash, block), hash) )

        
    def check_timestamp(self, blockchain, prevblockhandle, hash, block):
        if (block.blockheader.time <= blockchain.get_median_time_past(prevblockhandle.hash)):
            raise Exception("block's timestamp is smaller than the median of past %d block: %d <= %d" % (MEDIAN_TIME_SPAN, prevblockhandle.get_blockheader().time , prevblockhandle.get_median_time_past()))

    def check_tx_finalized(self, prevblockhandle, hash, block):
        height = prevblockhandle.get_height()+1
        #Check that all transactions are finalized (can this be done somewhere else?)
        for tx in block.transactions:
            if not tx.isfinal(height, block.blockheader.time):
                raise Exception("transaction is not final: %s" % str(hash_tx(tx)))
    
    def check_checkpoints(self, prevblockhandle, hash, block):
        height = prevblockhandle.get_height()+1
        if not verify_checkpoints(self.runmode, height, hash):
            raise Exception("blockchain checkpoint error: height:%d value:%s != %s" % (height, hash, str(get_checkpoint(self.runmode, height))))
Example #4
0
class TxDownloadService(Observable):
    EVT_ADDED_ORPHAN_TX = Observable.createevent()
    EVT_REMOVED_ORPHAN_TX = Observable.createevent()
    
    def __init__(self, node, blockchain, txpool, process_pool, log):
        Observable.__init__(self)
        self.node = node
        self.blockchain = blockchain
        self.txpool = txpool
        self.log = log
        self.items_to_download = deque()
        self.requested_tx = {}
        self.orphan_tx = {}
        self.process_pool = process_pool
        self.txverifier = TxVerifier(self.blockchain.database.runmode)
        
        node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_INV), self.on_inv)
        node.subscribe((VersionExchangeService.EVT_MESSAGE, MSG_TX), self.on_tx)
        
        self.node.subscribe (Node.EVT_DISCONNECTED, self.on_peer_disconnected)
        
    def on_inv(self, event):
        peer, message = event.handler, event.message
        for item in message.items:
            if item.type == INV_TX:
                if (not self.txpool.contains_transaction(item.hash) and 
                    not item.hash in self.requested_tx and 
                    not item.hash in self.orphan_tx):
                    self.requested_tx[item.hash] = peer
                    self.log.info("Downloading transactions from %s: %s" % (str(peer), str(item)))
                    self.node.send_message(peer, GetdataMessage([item]))
                  
    @asynch_method
    def on_tx(self, event):
        peer, message = event.handler, event.message
        hash = hash_tx(message.tx)
        self.log.info("on_tx hash:%s" % (str(hash))) 
        if (hash not in self.requested_tx):
            self.misbehaving(peer, "peer sending unrequest 'tx'")
            return
        del self.requested_tx[hash]
        #check for orphan transactions
        try:
            yield self.verified_add_tx(message.tx)
        except Exception as e:
            self.log.error("peer %s sending errorneous 'tx': %s" %(str(peer), str(e)))
            self.misbehaving(peer, "peer sending errorneous 'tx': ")
        #relay transaction
        
    def misbehaving(self, peer, reason):
        self.cleanup_peer_tasks(peer)
        self.node.misbehaving(peer, reason)

    def cleanup_peer_tasks(self, peer):
        toremove = [txhash for txhash, p in self.requested_tx.iteritems() if p==peer]
        for h in toremove:
            del self.requested_tx[txhash]
            
    def on_peer_disconnected(self, event):
        self.cleanup_peer_tasks(event.handler)

    @asynch_method
    def verified_add_tx(self, tx):
        txhash = hash_tx(tx)
        if self.txpool.contains_transaction(txhash):
            return
        self.txverifier.basic_checks(tx)
        if tx.iscoinbase():
            raise Exception("Coinbase transactions aren't allowed in memory pool")
        #check for orphan transactions.
        contains_txins = [self.blockchain.contains_transaction(txin.previous_output.hash) for txin in tx.in_list]
        if not all(contains_txins):
            self.log.info("Adding orphan tx %s" % str(txhash))
            self.orphan_tx[txhash] = tx
            self.fire(self.EVT_ADDED_ORPHAN_TX, hash=txhash)
            return
        
        self.log.info("Connecting tx %s" % str(txhash))
        self.blockchain.connect_pool_tx(tx, self.blockchain.get_height() + 1, self.process_pool)
        self.log.info("Adding tx %s" % str(tx))
        self.txpool.add_tx(txhash, tx)
        yield
Example #5
0
 def __init__(self, runmode):
     self.runmode = runmode
     self.block_serializer = BlockSerializer()
     self.tx_verifier = TxVerifier(self.runmode)
Example #6
0
class BlockVerifier():
    def __init__(self, runmode):
        self.runmode = runmode
        self.block_serializer = BlockSerializer()
        self.tx_verifier = TxVerifier(self.runmode)

    """
        basic_check: run tests that don't require context or the block parent.
    """

    def basic_checks(self, hash, block):
        self.check_size_limit(hash, block)
        self.check_proof_of_work(hash, block)
        self.check_coinbase(hash, block)
        self.check_transactions(hash, block)
        self.check_merkle_root(hash, block)

    def check_size_limit(self, hash, block):
        #FIXME: don't serialize the block here (add get_serialize_size() in serialize objects
        #or cache previoulsy serialized block)
        if (not block.rawdata):
            block.rawdata = self.block_serializer.serialize(block)
        if len(block.rawdata) > MAX_BLOCK_SIZE:
            raise Exception("block size limit exceeded")

    def check_proof_of_work(self, hash, block):
        target = block.blockheader.target()
        if (target <= Uint256.zero()
                or target > PROOF_OF_WORK_LIMIT[self.runmode]):
            raise Exception("proof of work: value out of range : %x" %
                            (block.blockheader.bits))
        if (hash > target):
            raise Exception(
                "proof of work: hash doesn't match target hash:%s, target:%s" %
                (hash, target))

    # not out of context: (check elsewhere)
    # def check_timestamp(self, hash, block):
    #     block.blockheader.time > time.time

    def check_coinbase(self, hash, block):
        if not len(block.transactions):
            raise Exception("Block has no transactions")
        if not block.transactions[0].iscoinbase():
            raise Exception("block's first transactions is not coinbase")
        for tx in block.transactions[1:]:
            if tx.iscoinbase():
                raise Exception("more than one coinbase")

    def check_transactions(self, hash, block):
        for tx in block.transactions:
            err = self.tx_verifier.basic_checks(tx)
            if err:
                return err

    def check_merkle_root(self, hash, block):
        merkle = compute_merkle_root(block.transactions)
        if merkle != block.blockheader.hash_merkle:
            raise Exception(
                "merkel root incorrect for block %s: %s != %s" %
                (str(hash), str(merkle), str(block.blockheader.hash_merkle)))

    """
        accept_block: check block after finding the parent block.
    """

    #AcceptBlock: main.cpp:1445
    def accept_block(self, hash, block, blockchain):
        prevblockhandle = blockchain.get_block_handle(
            block.blockheader.hash_prev)

        self.check_target(blockchain, prevblockhandle, hash, block)
        self.check_timestamp(blockchain, prevblockhandle, hash, block)
        self.check_tx_finalized(prevblockhandle, hash, block)
        self.check_checkpoints(prevblockhandle, hash, block)

    def check_target(self, blockchain, prevblockhandle, hash, block):
        #Check proof of work target
        if (blockchain.get_next_work_required(prevblockhandle.hash, block) !=
                block.blockheader.bits):
            raise Exception(
                "Incorrect difficulty target, found:%08x != required:%08x in block %s"
                % (block.blockheader.bits,
                   blockchain.get_next_work_required(prevblockhandle.hash,
                                                     block), hash))

    def check_timestamp(self, blockchain, prevblockhandle, hash, block):
        if (block.blockheader.time <= blockchain.get_median_time_past(
                prevblockhandle.hash)):
            raise Exception(
                "block's timestamp is smaller than the median of past %d block: %d <= %d"
                % (MEDIAN_TIME_SPAN, prevblockhandle.get_blockheader().time,
                   prevblockhandle.get_median_time_past()))

    def check_tx_finalized(self, prevblockhandle, hash, block):
        height = prevblockhandle.get_height() + 1
        #Check that all transactions are finalized (can this be done somewhere else?)
        for tx in block.transactions:
            if not tx.isfinal(height, block.blockheader.time):
                raise Exception("transaction is not final: %s" %
                                str(hash_tx(tx)))

    def check_checkpoints(self, prevblockhandle, hash, block):
        height = prevblockhandle.get_height() + 1
        if not verify_checkpoints(self.runmode, height, hash):
            raise Exception(
                "blockchain checkpoint error: height:%d value:%s != %s" %
                (height, hash, str(get_checkpoint(self.runmode, height))))