Validating the mining results of ENG token

in steem-engine •  6 months ago  (edited)

I've programmed a lot for the steem blockchain, thus I'm currently not very active in writing posts. As you may know I'm the developer behind scot and I have now also implemented Delegated Proof of Stake Mining for steem-engine tokens.

Currently two token can be staked to participate in ENG mining:

  • EM with a mining power of 1
  • EMFOUR with a mining power of 4

Every hour, the ENG token mining reward pool is send to 10 accounts which were randomly selected based on the staked mining power (in one round one account can be added more than once).

How does this work

Every hour, all mining token staker are obtained from the steem-engine api. Then a custom_json with id scot_create_claim and {"symbol":"ENG","type":"mining","N":10} as content is broadcasted from the engpool account.
image.png

The scot bot fetches this custom_json and uses the block number and the transaction id to calculate a seed. The seed is then used to initialize a random generator.

Then 10 accounts are randomly selected by generating random numbers from 0 to the staked mining power sum. The staked mining power of all accounts are summed up and sorted by the staked mining power and the loki steem-api id.

The results are broadcasted as a custom_json from the engpool account:
image.png

Lets assume that we have 3 miner, a has staked 1 EMFOUR with loki 2, b has staked 1 EMFOUR with loki 3 and c has staked 3 EM token with loki 1.

indexnamestaked mining powersumloki
1c331
1a472
1b4113

In the next step, we draw 5 times a random number between 0 and 11.
That account is selected which sum is closed above the random number.
Lets assume that the sequence is (I'm using integer random number for this example):

4, 2, 10, 7, 1

This means the winner are

random numberwinner
4a
2c
10b
7b
1c

Script to validate the most recent mining operation

In order to run this script, beem and steemengine must be installed.

The block number and the transaction id of the custom_json containing the mining outcome must be provided:

    block_num = 33505765  
    trx_id = "98df419edbd76079d1991bc58c2ea6db5077e4bd"

You can take a look at the custom_json here: https://steemd.com/tx/98df419edbd76079d1991bc58c2ea6db5077e4bd

Validation of the mining operation is only possible when:

  • nobody unstaked their mining token
  • nobody staked more mining token
  • no new accounts staked mining token
    As long these condition are true, any mining operation can be validated. The condition is check in the script by comparing N_accounts and staked_mining_power from the custom_json with the currently staked accounts.

The staked mining power is obtained by using the steem-engine api:

accounts_by_name = {}
miner_tokens = {"EM": 1, "EMFOUR": 4}
mining_power_sum = 0
for miner_token in miner_tokens:
    mining_power = miner_tokens[miner_token]
    token_object = Token(miner_token)
    token_holder = token_object.get_holder()
    for holder in token_holder:
        if float(holder["stake"]) == 0:
            continue
        if holder["account"] not in accounts_by_name:
            accounts_by_name[holder["account"]] = {"name": holder["account"], "staked_mining_power": float(holder["stake"]) * mining_power, "loki": holder["$loki"]}
        else:
            accounts_by_name[holder["account"]]["staked_mining_power"] += float(holder["stake"]) * mining_power
            if accounts_by_name[holder["account"]]["loki"] > holder["$loki"]:
                accounts_by_name[holder["account"]]["loki"] = holder["$loki"]                  
        mining_power_sum += float(holder["stake"]) * mining_power
      
accounts = []
for acc in accounts_by_name:
    accounts.append(accounts_by_name[acc])
sorted_accounts = sorted(accounts, key=lambda m: (m["staked_mining_power"], m["loki"]))

The accounts are firstly sorted by their staked mining power and secondly by the $loki id from the steem-engine account. This allows it to validate a mining operation, as the accounts are sorted in the same way.

The seed is set by:

def set_seed(seed):
    random.seed(a=seed, version=2)
block = Block(json_data["block_num"])
seed = hashlib.md5((json_data["trx_id"] + block["block_id"] + block["previous"]).encode()).hexdigest()     
set_seed(seed)

where block_num and trx_id are provided from the custom_json.
Finally, winner can be obtained by:

def get_random_range(start, end):
    return ((random.random() * (end-start + 1)) + start)

N = int(json_data["N"])
mining_power_sum = float(json_data["staked_mining_power"])
winner_accs = []
for i in range(N):
    x = get_random_range(0, mining_power_sum)
    sum_mining_power = 0
    winner_found = False
    for acc_data in sorted_accounts:
        sum_mining_power += acc_data["staked_mining_power"]
        
        if sum_mining_power > x and not winner_found:
            winner_accs.append(acc_data["name"])
            winner_found = True

The output of the script for the last custom_json is:
image.png

Script source

import time 
import json
import math
import random
from steemengine.tokenobject import Token
from beem.block import Block
import hashlib


def set_seed(seed):
    random.seed(a=seed, version=2)

def get_random_range(start, end):
    return ((random.random() * (end-start + 1)) + start)

if __name__ == "__main__":
    block_num = 33505765  
    trx_id = "98df419edbd76079d1991bc58c2ea6db5077e4bd"
    block = Block(block_num)
    
    data = None
    for trx in block.transactions:
        if trx["transaction_id"] ==trx_id:
            data = trx

    trx = data["operations"][0]["value"]
    json_data = json.loads(trx["json"])
    
    block = Block(json_data["block_num"])
    seed = hashlib.md5((json_data["trx_id"] + block["block_id"] + block["previous"]).encode()).hexdigest()     
    set_seed(seed)
    accounts_by_name = {}
    miner_tokens = {"EM": 1, "EMFOUR": 4}
    mining_power_sum = 0
    for miner_token in miner_tokens:
        mining_power = miner_tokens[miner_token]
        token_object = Token(miner_token)
        token_holder = token_object.get_holder()
        for holder in token_holder:
            if float(holder["stake"]) == 0:
                continue
            if holder["account"] not in accounts_by_name:
                accounts_by_name[holder["account"]] = {"name": holder["account"], "staked_mining_power": float(holder["stake"]) * mining_power, "loki": holder["$loki"]}
            else:
                accounts_by_name[holder["account"]]["staked_mining_power"] += float(holder["stake"]) * mining_power
                if accounts_by_name[holder["account"]]["loki"] > holder["$loki"]:
                    accounts_by_name[holder["account"]]["loki"] = holder["$loki"]                  
            mining_power_sum += float(holder["stake"]) * mining_power
          
    accounts = []
    for acc in accounts_by_name:
        accounts.append(accounts_by_name[acc])
    sorted_accounts = sorted(accounts, key=lambda m: (m["staked_mining_power"], m["loki"]))
    sum_mining_power = 0
    for acc_data in sorted_accounts:
        sum_mining_power += acc_data["staked_mining_power"]
    
    if int(json_data["N_accounts"]) != len(accounts_by_name) or abs(mining_power_sum - float(json_data["staked_mining_power"])) > 2 / 10 ** 6:
        print("staked accounts have changed and the mining cannot be validated...")
    else:
        N = int(json_data["N"])
        mining_power_sum = float(json_data["staked_mining_power"])
        winner_accs = []
        for i in range(N):
            x = get_random_range(0, mining_power_sum)
            sum_mining_power = 0
            winner_found = False
            
            for acc_data in sorted_accounts:
                sum_mining_power += acc_data["staked_mining_power"]
                
                if sum_mining_power > x and not winner_found:
                    winner_accs.append(acc_data["name"])
                    winner_found = True    
                    
        print("%d winner found" % len(winner_accs))
        if (winner_accs) == (json_data["winner"]):
            print("custom_json with %s was sucessfully validated. Winner are:" % trx_id)
            print(str(winner_accs))
        else:
            print("Custom_json could not be validated")
            print("winner from script:")
            print(str(winner_accs))
            print("winner from custom_json:")
            print(str(json_data["winner"]))            
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Thanks for everything you do on Steem that I don't understand. :3

uhu... +1

:)

Oh great, some insights on how the mining works. Thank you, highly appreciated! How many ENG tokens are distributed per hour, is the "1.1" above the total value or per winner? Is the amount of mining rewards constant each hour or does it change?

The reward pool is increased by 1 ENG token every 105 blocks. Every hour the complete pool is distributed to 10 accounts. The "1.1" is the value per winner. This value can vary slightly.

Thanks for the clarification!

This is amazing, hence why I started getting some yesterday. Though it was explained differently with @aggroed: "All you have to do is buy the EM token or the EMFOUR token, stake it, and it'll start mining"

I thought it would be trade for the EMFOUR token and get a daily % of the inflation pool. The more tokens the bigger the % and this was fixed.

Now you are adding another element of gamification where the more tokens you have, the more chances of being picked for the hourly reward pool. Is this correct?

This is correct, staking 1 EMFOUR token gives you 4 mining power, staking 1 EM token gives you 1 miningpower. You can check for the last custom_json here: https://steemd.com/@engpool what the current mining power of all accounts is.
Currently it is 3163.84326
So the change to win at least 1.1 ENG with 1 mining power staked is around 3% every hour.

:) I like the gamification element to this. Adds good value to the EM(four) tokens in the long run. Once these run out at this price, time to move on to Steem Engine tokens.

Am I right in assuming that there are currently no more than 4 million Steem engine tokens in circa. That only 100,000 extra are mined yearly. Is this fixed, or to be changed later? If so, what is the impact on EM(FOUR) token holders?

Sorry for all the questions, but after what you guys did with Splinterlands. I have full faith in your team's capacity. You have proved it to me :) I am ready to invest more in this amazing opportunity you are giving us.

To listen to the audio version of this article click on the play image.

Brought to you by @tts. If you find it useful please consider upvoting this reply.

Thank you so much for participating in the Partiko Delegation Plan Round 1! We really appreciate your support! As part of the delegation benefits, we just gave you a 3.00% upvote! Together, let’s change the world!

Hi, @holger80!

You just got a 3.42% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.

Hi @holger80!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your UA account score is currently 7.212 which ranks you at #55 across all Steem accounts.
Your rank has not changed in the last three days.

In our last Algorithmic Curation Round, consisting of 192 contributions, your post is ranked at #7.

Evaluation of your UA score:
  • Your follower network is great!
  • The readers appreciate your great work!
  • Great user engagement! You rock!

Feel free to join our @steem-ua Discord server

@holger80 do you need whole numbers to stake or can you stake fractional emfour and em?

Posted using Partiko Android