class BlockMessageSerializer(Serializer): block_serializer = BlockSerializer() def __init__(self): pass def serialize(self, block_msg): return (self.block_serializer.serialize(block_msg.block)) def deserialize(self, data, cursor=0): block, cursor = self.block_serializer.deserialize(data, cursor) return (BlockMessage(block), cursor)
def __init__(self, runmode): self.runmode = runmode self.block_serializer = BlockSerializer() self.tx_verifier = TxVerifier(self.runmode)
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))))
def test_block_serialize(self): blockheader = BlockHeader( version=1, hash_prev=Uint256.from_hexstr( "000000000fec081581146e8b16b275bfa52150ac4174a246cdf62694671ea7a3" ), hash_merkle=Uint256.from_hexstr( "0d9da162550fc45b1aaa00e933b23b3cbc7f37a9b2d2070d61235eaec11a926a" ), time=1301129903, bits=470809215, nonce=1280448751) tx1 = Tx( version=1, in_list=[ TxIn(previous_output=Outpoint.null(), script=Script([ Instruction(4, decodehexstr("7ffa0f1c")), Instruction(1, decodehexstr("4e")) ]), sequence=TxIn.NSEQUENCE_FINAL) ], out_list=[ TxOut( value=5002000000, script=Script([ Instruction( 65, decodehexstr( "049cc3cae30927c40598032044b9e9e25f4739b0d7ade62803f5e9cf075debc817e6d29f42c70d0a1beb1c904eaaa50ef885b011f9fbaa16ef288a7ad193e11567" )), Instruction(OP_CHECKSIG) ])) ], locktime=0) tx2 = Tx( version=1, in_list=[ TxIn(previous_output=Outpoint(hash=Uint256.from_hexstr( "17c5cb687ba453ab65e12cdc0d8721c70ab4678665eb200c50cb9f1e3207090e" ), index=0), script=Script([ Instruction( 72, decodehexstr( "3045022100ab2dc8932ca1d26f4cdac1feae09020a60ccc4d17b14e5fc5b21f3ab8c3a9cee022040a7208c172d19a19902280d66201c7fe2c3d8b2df7e23cc4e5b70fd52ecba2c01" )), Instruction( 65, decodehexstr( "04b77dd1f3a21cb3d067a7e76982a609d7310f8692f5d61346f3225323c425604a0c12862755335c49e392673106adfc5dfdee1e4d367f10353e8911fac687db3e" )) ]), sequence=TxIn.NSEQUENCE_FINAL) ], out_list=[ TxOut(value=24990000000, script=Script([ Instruction(OP_DUP), Instruction(OP_HASH160), Instruction( 20, decodehexstr( "4d8b17fbce571614be89df4bd872de892a479844")), Instruction(OP_EQUALVERIFY), Instruction(OP_CHECKSIG) ])), TxOut(value=5000000000, script=Script([ Instruction(OP_DUP), Instruction(OP_HASH160), Instruction( 20, decodehexstr( "fadad27c40adbe230f5e3c04d44a292975084831")), Instruction(OP_EQUALVERIFY), Instruction(OP_CHECKSIG) ])) ], locktime=0) blk = Block(blockheader, [tx1, tx2]) data = BlockSerializer().serialize(blk) self.assertEquals( data, decodehexstr( "01000000a3a71e679426f6cd46a27441ac5021a5bf75b2168b6e14811508ec0f000000006a921ac1ae5e23610d07d2b2a9377fbc3c3bb233e900aa1a5bc40f5562a19d0dafaa8d4d7ffa0f1cef18524c0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07047ffa0f1c014effffffff018076242a010000004341049cc3cae30927c40598032044b9e9e25f4739b0d7ade62803f5e9cf075debc817e6d29f42c70d0a1beb1c904eaaa50ef885b011f9fbaa16ef288a7ad193e11567ac0000000001000000010e0907321e9fcb500c20eb658667b40ac721870ddc2ce165ab53a47b68cbc517000000008b483045022100ab2dc8932ca1d26f4cdac1feae09020a60ccc4d17b14e5fc5b21f3ab8c3a9cee022040a7208c172d19a19902280d66201c7fe2c3d8b2df7e23cc4e5b70fd52ecba2c014104b77dd1f3a21cb3d067a7e76982a609d7310f8692f5d61346f3225323c425604a0c12862755335c49e392673106adfc5dfdee1e4d367f10353e8911fac687db3effffffff02802385d1050000001976a9144d8b17fbce571614be89df4bd872de892a47984488ac00f2052a010000001976a914fadad27c40adbe230f5e3c04d44a29297508483188ac00000000" ))
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))))