edicted Blog Banner

edicted

dsteem Tutorial Lesson 5: Inspecting Block Operations

### 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] + '
' }


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 ~E40Enot virtual~E41E.png.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).


Return from dsteem Tutorial Lesson 5: Inspecting Block Operations to edicted's Web3 Blog

dsteem Tutorial Lesson 5: Inspecting Block Operations was published on and last updated on 17 Oct 2018.