(Previous Post: Part 6)
There has been a lot of talk lately about Bandwidth on Steemit. It seems that certain anti-spam measures that are built into the system were maybe a little too aggressive. As a result, people were locked out of posting and powering up because they did not have enough allocated bandwidth remaining to commit the transaction to the blockchain.
A soft fork is in the works to address the aggressiveness aspect (as well as an integer overflow issue). But, let's take a look at how to calculate Bandwidth-related values in case you want to include those metrics into your Steemit Web App.
Note: This is information that I gathered by researching the Steem source code and other resources. If it is inaccurate in any way, then please leave a comment so that I can fix this article.
Bandwidth
Bandwidth is allocated per user based on the user's Steem Power (more accurately, based on their total vesting shares). There is a Maximum Virtual Bandwidth that represents 100% of the bytes that are available for all users to share. This can be calculated as:
number_of_blocks_per_week * maximum_block_size * current_reserve_ratio
maximum_block_size
(currently 65536) and current_reserve_ratio
(currently 416 as I write this) are values that can be found in the Dynamic Global Properties. The current_reserve_ratio
is the anti-spam number that fluctuates in response to bursts of new transactions.
number_of_blocks_per_week
is a constant, but is calculated as 20*60*24*7
(20 blocks per minute, 60 minutes per hour, 24 hours per day, 7 days per week... which comes out to be 201600).
So, in theory, there is a maximum of 5496215961600 bytes per week that can be used if the blockchain was 100% filled.
However, you don't actually have to calculate this every time because the Dynamic Global Properties also has a field named max_virtual_bandwidth
that has this value in it (well, this value multiplied by 1000000 so that up to six decimal places are represented in the integer value).
// gprops
{
current_reserve_ratio: 416,
max_virtual_bandwidth: "5496215961600000000",
maximum_block_size: 65536,
total_vesting_shares: "374404675433.834843 VESTS"
}
Remember back in Part 3 when I said that voting power is updated after each vote, but must be recalculated on-the-fly in order to get the most current value? Well, bandwidth works the same way. When you post to the blockchain, the witness nodes calculate your average bandwidth usage and store it at that point in time. However, your average will continue to tick downwards as time goes on (if you don't post anything further, that is). Therefore, any app that shows your current bandwidth must perform a calculation:
((total_seconds - seconds_since_last_update) * average_bandwidth) / total_seconds
total_seconds
, in this case, is the total number of seconds in a week (or 60*60*24*7
).
seconds_since_last_update
must be calculated as the difference between the current date/time (in UTC) and the last_bandwidth_update
date in the user's Account data.
average_bandwidth
also comes from the user's Account data, and, as stated before, represents the bandwidth usage at that point in time... but we're trying to figure out what the current value would be, hence the use of proportions in the calculation.
Note: The average bandwidth number is also saved as an integer with six decimal places represented. Divide by 1000000 in order to get the actual bytes of bandwidth.
// account data
{
average_bandwidth: "23420877883",
last_bandwidth_update: "2017-07-19T12:57:45",
received_vesting_shares: "57000.000000 VESTS",
vesting_shares: "228916.300865 VESTS"
}
The last piece of the puzzle is how to calculate the portion of the max_virtual_bandwidth
that is allocated to the user:
(vesting_shares + received_vesting_shares) * max_virtual_bandwidth / total_vesting_shares
vesting_shares
and received_vesting_shares
comes from the user's Account data. The suffix of "VESTS" will need to be trimmed off of the string in the data.
max_virtual_bandwidth
and total_vesting_shares
comes from the Dynamic Global Properties.
Let's see it in JavaScript
steem.api.getAccountsAsync([accountName])
.then(function (result) {
steem.api.getDynamicGlobalPropertiesAsync()
.then(function (gprops) {
const STEEMIT_BANDWIDTH_AVERAGE_WINDOW_SECONDS = 60 * 60 * 24 * 7;
let vestingShares = parseFloat(result[0].vesting_shares.replace(" VESTS", ""))
let receivedVestingShares = parseFloat(result[0].received_vesting_shares.replace(" VESTS", ""))
let totalVestingShares = parseFloat(gprops.total_vesting_shares.replace(" VESTS", ""))
let max_virtual_bandwidth = parseInt(gprops.max_virtual_bandwidth, 10)
let average_bandwidth = parseInt(result[0].average_bandwidth, 10)
let delta_time = (new Date - new Date(result[0].last_bandwidth_update + "Z")) / 1000
let bandwidthAllocated = (max_virtual_bandwidth * (vestingShares + receivedVestingShares) / totalVestingShares)
bandwidthAllocated = Math.round(bandwidthAllocated / 1000000);
let new_bandwidth = 0
if (delta_time < STEEMIT_BANDWIDTH_AVERAGE_WINDOW_SECONDS) {
new_bandwidth = (((STEEMIT_BANDWIDTH_AVERAGE_WINDOW_SECONDS - delta_time)*average_bandwidth)/STEEMIT_BANDWIDTH_AVERAGE_WINDOW_SECONDS)
}
new_bandwidth = Math.round(new_bandwidth / 1000000)
console.log("current bandwidth used", new_bandwidth)
console.log("current bandwidth allocated", bandwidthAllocated)
console.log("bandwidth % used", 100 * new_bandwidth / bandwidthAllocated)
console.log("bandwidth % remaining", 100 - (100 * new_bandwidth / bandwidthAllocated))
})
})
Results:
current bandwidth used 18826
current bandwidth allocated 4197217
bandwidth % used 0.4485353032735739
bandwidth % remaining 99.55146469672643
And, just like the Voting Power post, improvements to this would be to periodically recalculate the value using a timer so that the user always sees their latest bandwidth usage.
(Next Post: Part 8)