Hive As A Black Box - Operations

Context

In these posts I try looking at Hive as a black box.
I keep poking at it, to get some better insight on how it works.

Hive Ops

Heard on the street: Hive has ops.
Virtual ops on the block.
I investigate. ๐Ÿ˜Ž

block_api

I've demonstrated how it should be possible to build a whole game engine around block_ap.get_block_range, by just looking at all operations in a live stream.

  • api.py:
import requests

def get_block_range(start, count, url):
    data = '{"jsonrpc":"2.0", "method":"block_api.get_block_range","params":{"starting_block_num":'+str(start)+',"count": '+str(count)+'},"id":1}'
    return requests.post(url, data)

Same 'api' as before. Returns the response raw.

  • get_ops:
def get_ops_with_block_range(start, count, url):
    response = api.get_block_range(start, count, url)
    blocks = response.json()['result']['blocks']
    ops = []
    for block in blocks:
        for transaction in block['transactions']:            
            for operation in transaction['operations']:                
                ops.append(operation)
    return ops

Operations are in transactions, transactions are in blocks.
I need to iterate through that nest to collect all ops.

condenser

condenser_api.get_ops_in_block can also return ops.

  • api:
def condenser_get_ops_in_block(block, url, only_virtual):
    data = '{"jsonrpc":"2.0", "method":"condenser_api.get_ops_in_block", "params":['+str(block)+','+only_virtual+'], "id":1}'
    return requests.post(url, data)  
  • get_ops:
def get_ops_with_condenser(num, url, only_virtual= 'false'):
    response = api.condenser_get_ops_in_block(num, url, only_virtual)
    ops = response.json()['result']
    return ops

That should return the same ops as with block_api get_ops...

Test

import time

url = 'https://api.hive.blog'
print(len(get_ops_with_block_range(89040499, 1, url)))
time.sleep(2)
print(len(get_ops_with_condenser(89040499, url)))

returns:

91
143

Virtual Operations

There is a difference of 52.

You can read more on virtual operations in the documentation. btw: the examples there don't really work ๐Ÿ™„, but:

Virtual operations (curation rewards, etc) are derived from blockchain activity, but arenโ€™t actually stored as operations themselves. They happen based on consensus from the blockchain based on other user initiated operations. These virtual operations are NOT available on the head block, so a 100% live feed of this information would not be possible. In order then to follow these operations you would have to stream the last_irreversible_block. To get a feed of virtual operations, each of the block transactions needs to be investigated for the type of the operations.

I would explain it more like this:

'Normal' operations are user operations. They are explicit.
They need to be signed and broadcast by a user: Alice makes a post.
Virtual operations are implicit: When Alice makes a post, determines when the post payout will be.
Post and curation rewards must happen exactly 7 day later and are accounted for as virtual operations.

But this is just a coding adventure, not a guide...

Test

print(len(get_ops_with_condenser(89040499, url, only_virtual = 'true')))
>> 52

account_history

In more practical terms, the best endpoint I could find to get to virtual operations:
account_history_api.enum_virtual_ops
I think account_history plugin (not api) must be activated on all nodes by default...
Information behind all of this is scarce...

  • api.py:
def account_history_enum_virtual_ops(start, stop, url):
    data = '{"jsonrpc":"2.0", "method":"account_history_api.enum_virtual_ops", "params":{"block_range_begin":'+str(start)+',"block_range_end":'+str(stop)+'}, "id":1}'
    return requests.post(url, data) 
  • get_ops:
def get_ops_with_enum(start, stop, url):
    response = api.account_history_enum_virtual_ops(start, stop, url)
    return response.json()['result']['ops']

Once again, the documentation is wrong.
The ['ops'] is important, as the response actually looks like this:

next_block_range_begin
next_operation_begin
ops
ops_by_block

'next' is used with 'limit' for pagination.
Anyways, after navigating to 'ops':

print(len(get_ops_with_enum(89040499, 89040500, url)))
>> 52

Reversibility

If you actually visited the links from above, you probably noticed reversibility being mentioned.
In the documentation there are parts about head block and such.
Blocks used to be reversible for 1 minute.
That was an issue. The HAF has a huge module 'fork manager' to deal with reversibility.
But the Hive protocol got updated and blocks are kind of insta-irreversible now.
...for this post I am just hacking away at operations. I don't understand it well enough to try explaining things, but I'll investigate further...

Conclusion

There's more to it, than just looking at blocks.
Virtual operations are not accessible by block_api.
In terms of automating something: If all you ever need is user operations, streaming block_api could be reliable enough. Virtual ops are mostly dealing with delayed things like posting rewards - depending on what you want to build, it could matter...

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center