dsteem Tutorial Lesson 5: Inspecting Block Operations

in #utopian-io6 years ago

Repository

https://github.com/jnordberg/dsteem

You will learn how to:

  • Retrieve blocks from the blockchain.
  • Inspect the operations of those blocks.
  • Inspect the history of an account.
  • Learn how to use the client.database.call() method.
  • Interact with the Steemit API without the complications of Node.JS

Requirements

  • HTML
  • JavaScript

Difficulty

  • Basic

Curriculum

Lesson 0: Steem Tutorials Are Severely Lacking
Lesson 1: Filtering Tabs
Lesson 2: Hello World
Lesson 3: Dissecting Discussions
Lesson 4: Steem Stake Sterilizer

Proof of Work Done

https://gist.github.com/edict3d/4e8abde562983f15440a5dba46bef623


tutorial-keyboard-dsteem-utopian-io.jpg

Boot up the Inspect.html file in a browser. Feel free to change the default author and permlink to something else if you like. All of the output from this code gets kicked out to the console. It's much easier to read if you expand the console a bit. It should look something like this in the browser:


inspect.html open.png

Pushing the "Inspect Account" button will retrieve the account of the author and log it in the console. In the last tutorial we accomplished this by using the client.database.getAccounts() method. However, this time we are going to use the client.database.call() method.

client.database.call()

This part of the API lets you call any method listed in the Appbase API Definitions:
https://developers.steem.io/apidefinitions/
There are a lot of methods defined here. The organization clearly needs work.

However, the client.database.call() method is very powerful because it can be used in so many ways. Simply type the name of the method followed by an array of the required arguments. Here is a list of the client.database.call() functions I use in the Inspect.html file:

client.database.call('get_content', [author, permlink]).then(function(discussion){})

client.database.call('get_account_history', [author, -1, 100]).then(function(history_results){})

client.database.call('get_block_header', [block_num]).then(function(header){})

client.database.call('get_ops_in_block', [block_num, false]).then(function(operations){})

client.database.call('get_block', [block_num]).then(function(block){})

client.database.call('get_accounts', [[author]]).then(function(accounts){})

One of the most important methods I've found is get_content_replies.

 client.database.call('get_content_replies', [author, permlink]).then(function(replies){})

This is the only way I've found to get the tier 1 replies (comments) of a Discussion. In the previous lesson, using a DiscussionQueryCategory of "comments" with getDiscussions() doesn't work. This setup only returns the comments of an Author and will not work if the main tag is anything but an Author.


Pushing the "Inspect Discussion" button logs the discussion correlated to the given author and permlink in the console. Again, instead of using getDiscussion() like we did in the previous lesson we are using:

client.database.call('get_content',[author, permlink]).then(function(discussion){})

So far this is nothing new.
Now, push the "Inspect History" button.

Inspect History.png


This button should not only log the last 100 operations of the given account, it should also create buttons on the left side that correlate to those operations.


Inspect History 2.png

The buttons are designed to do one thing and one thing only:
Populate the empty number field with the block number of that operation.
This is accomplished using the following code:

    for(var i = history_results.length-1; i >= 0; i--){
        var operation = history_results[i][1]
        p.innerHTML += ' <button id="' + operation.block
                + '" onClick="importBlock(this.id)"'
                + '>' + operation.op[0] + '</button><br>'
    }

The Inspect Block and Inspect Operations will not work unless a valid block number is typed into the empty number field. Click one of the operation buttons or enter a block_num by hand before attempting to retrieve a block.

At first, I was only using the get_block method to return the entire block. I then added some code to verify that I indeed had received the correct block. In order to do this I had to scan all the operations of the block and match the operation with the author.

This proved to be much more confusing than I thought it should be. It required 2 nested loops instead of a standalone loop and certain operations were not finding matches. That's when I realized that the blocks I was getting didn't contain "virtual operations".

virtual operation names.png


What's the difference between a virtual operation and a regular operation? I'm not exactly sure, but it looks like it mostly revolves around inflation being created.


operation names (not virtual).png


I must admit I was very confused that Steemit Inc would be sending me blocks that didn't contain all the operations, especially ones that were so important. Every time I tried to track a reward my code couldn't find the right operation inside the block because it wasn't being included in the first place.

This led me to find the get_ops_in_block method.

get_ops_in_block

client.database.call('get_ops_in_block', [block_num, false]).then(function(operations){})

This method is the best way to view the operations in a block. Instead of needing two loops to traverse it, only one is needed. The boolean argument is named only_virtual. By specifying a second argument of false you are saying you'd like steemit to give you all the operations and not just the virtual ones.

get-ops-in-block.png

I then moved the logic to match account history to block operations into function inspectOperations(){ on line 58.

    for(var i = 0; i < operations.length; i++){
        var operation = operations[i]
        var block_number = operation.block
        var op = operation.op
        var action = op[0] // vote, curation_reward, comment, etc
        var info = op[1] // voter, curator, author, permlink, etc
        if(         info.author === author
                ||  info.curator === author
                ||  info.voter === author
                ||  info.from === author
                ||  info.to === author
                ||  info.account === author     ){
            console.log('Author match found at index: ' + i);
        }
    }

When you click one of the operation buttons to fill the block_num field then push the "Inspect Block Operations" button it should log the operations and scan them to find a match. The match will then also be logged.


match op to history.png

Here we see the most recent vote I made occurred on block 26880386. When we retrieve the operations from this block the scan finds a match at operation 44. We can then traverse the operations object directly in the console to confirm this match.


match vote 2.png


{voter: "edicted", author: "edicted", permlink: "re-programmingvalue-re-edicted-steem-problems-can-t-be-fixed-with-hard-forks-20181016t231934592z", weight: 1000}

match vote.png


Upvoted myself at 10% strength. Everything seems to add up.

Conclusion

It becomes obvious after using the API that it needs a lot of work. Time and time again I run into problems that I shouldn't be having to find the solution for. This time it was blocks not containing virtual operations. Hopefully in the future Steemit Inc will sort this stuff out. In the meantime, hopefully I can save a few developers some time having to troubleshoot the many flaws and omissions of this interface.

The next lesson will involve using get_content_replies to download the entire tree of a top level blog discussion. We'll also be learning how to retrieve a block using nothing but a timestamp (functionality that surprisingly does not exist in the API yet).

Sort:  

I thank you for your contribution. Here are my thoughts. Note that, my thoughts are my personal ideas on your post and they are not directly related to the review and scoring unlike the answers I gave in the questionnaire;

  • Structure

    • It might be better to categorize the post with more topics. I do not fully advise it, but consider it.
  • Content

    • As this is a tutorial, I advise you to focus less on your experiments. Of course, you can share them but tutorials' main goal is usually teaching a way. Talking about experiments drives it more to a blog post. Still, you are the one that chooses, mine is only a suggestion. Either won't affect the quality of your post.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thank you for your review, @yokunjon!

So far this week you've reviewed 1 contributions. Keep up the good work!

Nice work.

I think having separate get_block (without virtual ops), get_ops [false] (with virtual operations), and get_ops [true] (just virtual operations) is deliberate and actually useful. It gives more flexibility as to what you're requesting.

However the point I would note is that there is at least one minor difference in the data obtained between get_block and get_ops. For vote transactions you get the signature with get_block. This is not the case for get_ops.

Good work! Maybe I can make use of this myself one day!

Hi @edicted!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @edicted!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.23
TRX 0.21
JST 0.035
BTC 98823.85
ETH 3347.31
USDT 1.00
SBD 3.15