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:
- push a tx to the network
- listen to
new_tx_event(tx)
get my tx hash - listen to
new_block_event(block)
find my tx hash is accepted in the block - start to confirm payment counting new blocks as confirmations
- find out later that the block got replaced/became orphaned
Your thoughts?