When viewing the stats on https://blocktivity.info/ its seems that BLURT has almost reached the same activity as HIVE:
The blockchain operation counts are correct this time, so I wrote a python script to count the different operation types.
import sys
from datetime import datetime, timedelta
from prettytable import PrettyTable
import argparse
from timeit import default_timer as timer
import logging
from beem.blockchain import Blockchain
from beem.block import Block
from beem import Hive, Blurt, Steem
from beem.utils import parse_time
from beem.nodelist import NodeList
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def parse_args(args=None):
d = 'Show op type stats for either hive, blurt or steem.'
parser = argparse.ArgumentParser(description=d)
parser.add_argument('blockchain', type=str, nargs='?',
default=sys.stdin,
help='Blockchain (hive, blurt or steem)')
return parser.parse_args(args)
def main(args=None):
args = parse_args(args)
blockchain = args.blockchain
nodelist = NodeList()
nodelist.update_nodes(weights={"block": 1})
if blockchain == "hive" or blockchain is None:
max_batch_size = 50
threading = False
thread_num = 16
block_debug = 1000
nodes = nodelist.get_hive_nodes()
blk_inst = Hive(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
elif blockchain == "blurt":
max_batch_size = None
threading = False
thread_num = 8
block_debug = 20
nodes = ["https://rpc.blurt.buzz/", "https://api.blurt.blog", "https://rpc.blurtworld.com", "https://rpc.blurtworld.com"]
blk_inst = Blurt(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
elif blockchain == "steem":
max_batch_size = 50
threading = False
thread_num = 16
block_debug = 1000
nodes = nodelist.get_steem_nodes()
blk_inst = Steem(node=nodes, num_retries=3, num_retries_call=3, timeout=30)
else:
raise Exception("Wrong parameter, can be hive, blurt or steem")
print(blk_inst)
block_count = 0
total_ops = 0
total_trx = 0
duration_s = 60 * 60 * 1
blocksperday = int(duration_s / 3)
blockchain = Blockchain(blockchain_instance=blk_inst, )
current_block_num = blockchain.get_current_block_num()
last_block_id = current_block_num - blocksperday
last_block = Block(last_block_id, blockchain_instance=blk_inst)
stopTime = last_block.time() + timedelta(seconds=duration_s)
start = timer()
op_stats = {}
for entry in blockchain.blocks(start=last_block_id, max_batch_size=max_batch_size, threading=threading, thread_num=thread_num):
if "block" in entry:
block_time = parse_time(entry["block"]["timestamp"])
else:
block_time = entry["timestamp"]
if block_time > stopTime:
break
block_count += 1
if "block" in entry:
trxs = entry["block"]["transactions"]
else:
trxs = entry["transactions"]
for tx in trxs:
total_trx += 1
for op in tx["operations"]:
if "_operation" in op["type"]:
op_type = op["type"][:-10]
else:
op_type = op["type"]
if op_type in op_stats:
op_stats[op_type] += 1
else:
op_stats[op_type] = 1
total_ops += 1
ops_per_day = total_ops / block_count * blocksperday
if block_count % (block_debug) == 0:
print("%d blocks remaining... estimated ops per day: %.1f" % (blocksperday - block_count, ops_per_day))
duration = timer() - start
t = PrettyTable(["Type", "Count", "percentage"])
t.align = "l"
op_list = []
for o in op_stats:
op_list.append({"type": o, "n": op_stats[o], "perc": op_stats[o] / total_ops * 100})
op_list_sorted = sorted(op_list, key=lambda x: x['n'], reverse=True)
for op in op_list_sorted:
t.add_row([op["type"], op["n"], "%.2f %%" % op["perc"]])
print(t)
if __name__ == '__main__':
sys.exit(main())
You need to install beem (pip3 install beem
) and can then start the script (after stored it as blockstats.py
):
python3 blockstats.py hive
python3 blockstats.py blurt
As the API speed of BLURT is limited, I limit the time period to the last hour.
(It can be adapted at line 62).
Results
Hive
+-------------------------+-------+------------+
| Type | Count | percentage |
+-------------------------+-------+------------+
| custom_json | 22413 | 58.24 % |
| vote | 13786 | 35.82 % |
| comment | 716 | 1.86 % |
| transfer | 554 | 1.44 % |
| claim_reward_balance | 398 | 1.03 % |
| comment_options | 209 | 0.54 % |
| limit_order_create | 86 | 0.22 % |
| claim_account | 84 | 0.22 % |
| feed_publish | 76 | 0.20 % |
| limit_order_cancel | 57 | 0.15 % |
| delegate_vesting_shares | 27 | 0.07 % |
| create_claimed_account | 11 | 0.03 % |
| transfer_to_vesting | 10 | 0.03 % |
| update_proposal_votes | 10 | 0.03 % |
| convert | 9 | 0.02 % |
| account_update | 8 | 0.02 % |
| account_update2 | 8 | 0.02 % |
| delete_comment | 6 | 0.02 % |
| witness_set_properties | 6 | 0.02 % |
| account_witness_vote | 5 | 0.01 % |
| withdraw_vesting | 4 | 0.01 % |
| transfer_to_savings | 1 | 0.00 % |
| transfer_from_savings | 1 | 0.00 % |
+-------------------------+-------+------------+
BLURT
+------------------------+-------+------------+
| Type | Count | percentage |
+------------------------+-------+------------+
| witness_set_properties | 37589 | 99.88 % |
| claim_reward_balance | 16 | 0.04 % |
| vote | 14 | 0.04 % |
| comment | 9 | 0.02 % |
| comment_options | 4 | 0.01 % |
| transfer | 1 | 0.00 % |
| custom_json | 1 | 0.00 % |
+------------------------+-------+------------+
Spam attack on BLURT
This issue indicates that the broadcasted witness_set_properties operations are indeed an attack to the BLURT blockchain:
https://gitlab.com/blurt/blurt/-/issues/89
Transaction fee on BLURT
The bandwidth has increased from 0.01 BLURT/KByte(as far as I remember) to 0.250 BLURT/KByte. As the transaction fee has to be paid in front for every broadcasted operation from the users wallet, this 25x increase is painful.
Blurt price
The BLURT price has reacted to the spam attack and is almost 1 Sat/BLURT:
Conclusion
99.88 % of all operations on the BLURT blockchain are spam witness_set_properties operations. 9 posts/comments have been written in the last hour and 14 votes have been cast. This is not much in comparison to 716 posts / comments and 13786 votes on Hive. This indicates that the attack works and blurt user have almost stopped using the chain. This is also indicated by the price movement.
The strange thing is that the attacker seems not to pay a bandwidth fee for their attack:
https://blocks.blurtwallet.com/#/@gbemyktquwtpfizr
As it can be seen that the blurt wallet is almost empty, it seems to me that the attacker have found a way to cast their spam ops for free.
I found the problem:
https://gitlab.com/blurt/blurt/-/blob/dev/libraries/plugins/rc/include/blurt/plugins/rc/resource_sizes.hpp :
witness_set_properties
has no
blurt::plugins::rc::state_object_size_info
properties, which means that broadcasting a witness_set_properties op seems not to costs any blurt fee.