Skip to content

Instantly share code, notes, and snippets.

@poctek
Last active May 13, 2019 18:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save poctek/1a8315698349852b7e623a467dabe1f1 to your computer and use it in GitHub Desktop.
Save poctek/1a8315698349852b7e623a467dabe1f1 to your computer and use it in GitHub Desktop.
# frozen_string_literal: true
module Payouts
module Operations
module Unlocker
class CalculateRewards
include Dry::Monads::Result::Mixin
include Dry::Monads::List::Mixin
include Dry::Monads::Do.for(:call)
include Import[
:eth_nodes,
'operations.shared.calculate_base_reward'
]
include Payouts::Modules::RPC
include Payouts::Modules::Logging
class Contract < Dry::Validation::Contract
params do
required(:blocks).each do
schema do
required(:network_block).filled { type?(Types::RPC::Block) | type?(Types::RPC::Uncle) }
required(:candidate).filled(type?: DB::Block)
end
end
end
end
def call(input)
yield Contract.new.(input)
calculate_rewards(input[:blocks_data]).fmap do |data_with_rewards|
input.merge(blocks_data: data_with_rewards)
end
end
private
def calculate_rewards(blocks)
rewards = blocks.each_with_object([]) do |block_data, memo|
block = block_data[:network_block]
reward = case block
when Types::RPC::Uncle then calculate_uncle_reward(block)
when Types::RPC::Block then calculate_full_block_reward(block)
end
reward
.fmap { |value| memo << block_data.merge(reward: value) }
.or { |error| return Failure(error) }
end
Success(rewards)
end
def calculate_full_block_reward(block)
included_transactions_reward(block.transactions).fmap do |txs_reward|
base_reward = base_reward(block.number)
base_reward +
included_uncles_reward(block.uncles, base_reward) +
txs_reward
end
end
def base_reward(number)
calculate_base_reward.(number: number).value!
end
def included_transactions_reward(transactions)
List::Task[*tasks(transactions)].traverse
.fmap { |rewards| rewards.value.sum }
.to_result
end
def tasks(transactions)
node = eth_node
transactions.map do |transaction|
Dry::Monads::Task[:io] do
hash = transaction[:hash]
# initialize new connection for each thread
receipt = node.get_transaction_receipt(hash, persistent: false)
receipt.or do |error|
message = "Failed to fetch transaction #{hash}: #{error}"
log_error_message(message, hash: hash)
raise StandardError, error
end
receipt.bind { |result| transaction.gas_price * result.gas_used }
end
end
end
def extract_tasks_result(list)
list.value.sum
end
def included_uncles_reward(uncles, base_reward)
uncles.size * (base_reward / 32)
end
def calculate_uncle_reward(uncle)
value = base_reward(uncle.number) * (8 - (uncle.parent_number - uncle.number)) / 8
Success(value)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment