1

I have a giant hole in my understanding!

I recently started using https://github.com/sebicas/bitcoin-sniffer

which is a Python script that lets you connect to any node on the Bitcoin Network and triggers events:

  • new_block_event(block): Triggered when a Block is found
  • new_tx_event(tx): Triggered when a Transactions is found

On inspecting a new block I was quite surprised to find that the object does not include block.height!

Then I found this question: Is block height always sequential?

Quote:

Block height is by definition sequential in the sense that adjacent blocks will always have heights that differ by 1. But there is a chicken-and-egg problem: in order to compute the height, you have to have access to all the blocks in between the genesis block and the current one. (The height is not recorded in the block itself.)

There's also the issue that multiple blocks can have the same height, if they are on different branches of the chain. So just because you have one block at every height, doesn't mean you have all the blocks on the main chain; some of them might be on orphan branches.


So my Question is

How can i tell if the block is official / not orphaned and that it will not be replaced by a 'better' block?


I see that each block contains a previous block hash so my reasoning is that if a block was orphaned then the next blocks previous block hash would not be that blocks hash but another. But using that as a test to see If the last block became part of the network would put the check (time) back by one block... which is not realtime nor ideal

The bitcoin-sniffer does have one curious function for a block message called block.is_valid at the end of the CBlock class:

class CBlock(object):
    def __init__(self):
        self.nVersion = 1
        self.hashPrevBlock = 0
        self.hashMerkleRoot = 0
        self.nTime = 0
        self.nBits = 0
        self.nNonce = 0
        self.vtx = []
        self.sha256 = None
        self.hash = None
    def deserialize(self, f):
        self.nVersion = struct.unpack("<i", f.read(4))[0]
        self.hashPrevBlock = deser_uint256(f)
        self.hashMerkleRoot = deser_uint256(f)
        self.nTime = struct.unpack("<I", f.read(4))[0]
        self.nBits = struct.unpack("<I", f.read(4))[0]
        self.nNonce = struct.unpack("<I", f.read(4))[0]
        self.vtx = deser_vector(f, CTransaction)
    def serialize(self):
        r = ""
        r += struct.pack("<i", self.nVersion)
        r += ser_uint256(self.hashPrevBlock)
        r += ser_uint256(self.hashMerkleRoot)
        r += struct.pack("<I", self.nTime)
        r += struct.pack("<I", self.nBits)
        r += struct.pack("<I", self.nNonce)
        r += ser_vector(self.vtx)
        return r
    def calc_sha256(self):
        if self.sha256 is None:
            r = ""
            r += struct.pack("<i", self.nVersion)
            r += ser_uint256(self.hashPrevBlock)
            r += ser_uint256(self.hashMerkleRoot)
            r += struct.pack("<I", self.nTime)
            r += struct.pack("<I", self.nBits)
            r += struct.pack("<I", self.nNonce)
            self.sha256 = uint256_from_str(hash256(r))
            self.hash = hash256(r)[::-1].encode('hex_codec')
    def is_valid(self):
        self.calc_sha256()
        target = uint256_from_compact(self.nBits)
        if self.sha256 > target:
            return False
        hashes = []
        for tx in self.vtx:
            if not tx.is_valid():
                return False
            tx.calc_sha256()
            hashes.append(ser_uint256(tx.sha256))
        while len(hashes) > 1:
            newhashes = []
            for i in xrange(0, len(hashes), 2):
                i2 = min(i+1, len(hashes)-1)
                newhashes.append(hash256(hashes[i] + hashes[i2]))
            hashes = newhashes
        if uint256_from_str(hashes[0]) != self.hashMerkleRoot:
            return False
        return True
    def __repr__(self):
        return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx))

The function is 'way-over-my-head' to be honest but it looks like it might be trying to determine weather the block can be trusted (as in; not tampered with as its querying the hashMerkleRoot which is a one way hash mechanism) but, I'm not knowledgeable enough to to tell for sure if the is_valid is doing more like (determining that the block is official)

The last thing I want to do is:

  1. push a tx to the network
  2. listen to new_tx_event(tx) get my tx hash
  3. listen to new_block_event(block) find my tx hash is accepted in the block
  4. start to confirm payment counting new blocks as confirmations
  5. find out later that the block got replaced/became orphaned

Your thoughts?

1
  • 2
    This is an issue you cannot avoid. It is always possible for a block to be replaced or orphaned at a later time. However, after several confirmations, it is unlikely. Commented Sep 24, 2016 at 21:15

1 Answer 1

3

How can i tell if the block is official / not orphaned and that it will not be replaced by a 'better' block?

As Nate notes, there's no guarantee that any block will be part of the best chain in the future. Here's what you should ask: Is this block part of the highest-work valid blockchain?

Here's how you determine that: you get the blocks, and connect them together. You sum up the work in each block in the blockchain. You check that they form a valid blockchain, and don't break any rules. You constantly look for more blocks, and try to assemble better, higher-work, valid blockchains.

I'm not knowledgeable enough to to tell for sure if the is_valid is doing more like (determining that the block is official)

Here's a list of things that code doesn't check:

  • Does a transaction spend more money than exists in an address?
  • Do transactions inputs actually exist?
  • Does this block connect to anything?

In fact, you can provide it a difficulty-1 block that doesn't connect to anything, and it will tell you that the block is valid.

4
  • Does this block connect to anything? wouldn't you have to wait for the next block to do this? Commented Sep 26, 2016 at 18:46
  • 2
    @BenMuircroft To check if it has children, yes. To check if it has parents, no. I can look at all of the blocks I already have, and see if the new block's prevBlock matches any block's hash.
    – Nick ODell
    Commented Sep 26, 2016 at 19:33
  • 2
    @BenMuircroft Also, note that orphan blocks typically have parents: bitcoin.stackexchange.com/questions/32110/… (Yes, that doesn't make any sense.)
    – Nick ODell
    Commented Sep 26, 2016 at 19:36
  • Just a note on the terminology: "orphan" is used despite orphan blocks having parents. In the real world, orphans have (biological) parents; the distinction is that their parents have abandoned them. Thus, orphan blocks are blocks who have been cast aside by their parents, no longer being part of the canonical blockchain.
    – Jivan Pal
    Commented Feb 11, 2021 at 18:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.