Skip to content

Commit

Permalink
[ᚬmaster] Rc/v0.9.0 (#540)
Browse files Browse the repository at this point in the history
[ᚬmaster] Rc/v0.9.0
  • Loading branch information
shaojunda authored Jan 2, 2020
2 parents 1c9e0b4 + 5b3bc50 commit da8381c
Show file tree
Hide file tree
Showing 56 changed files with 1,355 additions and 93 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ BLOCK_PROCESS_LOOP_INTERVAL="1"
DEFAULT_EPOCH_REWARD="1_917_808_21917808"
CKB_NET_MODE="mainnet"
HOMEPAGE_BLOCK_RECORDS_COUNT="15"
HOMEPAGE_TRANSACTIONS_RECORDS_COUNT="15"
5 changes: 5 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: zhulik/[email protected]
- name: Set up Redis
with:
redis version: "5"
number of databases: 2
- uses: actions/checkout@v1
- name: Set up Ruby 2.6.4
run: |
Expand Down
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
# [0.9.0](https://github.com/shaojunda/ckb-explorer/compare/v0.8.3...v0.9.0) (2020-01-02)


### Bug Fixes

* fix BlockStatistic worker bug ([550308a](https://github.com/shaojunda/ckb-explorer/commit/550308a))
* fix cell status not changed ([7522100](https://github.com/shaojunda/ckb-explorer/commit/7522100))


### Features

* add block list serializer ([8d729d7](https://github.com/shaojunda/ckb-explorer/commit/8d729d7))
* add block statistic generator service ([db91614](https://github.com/shaojunda/ckb-explorer/commit/db91614))
* add block timestamp to dao event ([a40453b](https://github.com/shaojunda/ckb-explorer/commit/a40453b))
* add capacity_involved column to ckb transaction ([7bcd7a5](https://github.com/shaojunda/ckb-explorer/commit/7bcd7a5))
* add chart forked event processor ([83927bf](https://github.com/shaojunda/ckb-explorer/commit/83927bf))
* add epoch statistic generator service ([a99926c](https://github.com/shaojunda/ckb-explorer/commit/a99926c))
* add estimated_apc to dao contract ([cb31d1c](https://github.com/shaojunda/ckb-explorer/commit/cb31d1c))
* add external stats api ([d9a3fdb](https://github.com/shaojunda/ckb-explorer/commit/d9a3fdb))
* add forked event model ([2cf8bff](https://github.com/shaojunda/ckb-explorer/commit/2cf8bff))
* add hash rate to epoch statistic ([e0746df](https://github.com/shaojunda/ckb-explorer/commit/e0746df))
* add index action to ckb transactions controller ([e71fef7](https://github.com/shaojunda/ckb-explorer/commit/e71fef7))
* add live_cell_changes to block and ckb_transaction ([2f04c62](https://github.com/shaojunda/ckb-explorer/commit/2f04c62))
* add live_cell_changes to forked blocks ([a2d42b2](https://github.com/shaojunda/ckb-explorer/commit/a2d42b2))
* add more field to daily statistics ([3912139](https://github.com/shaojunda/ckb-explorer/commit/3912139))
* add pagination to ckb transactions controller ([2251ba2](https://github.com/shaojunda/ckb-explorer/commit/2251ba2))
* add ratio scale ([3b68742](https://github.com/shaojunda/ckb-explorer/commit/3b68742))
* calculate estimated apc ([4a4f04f](https://github.com/shaojunda/ckb-explorer/commit/4a4f04f))
* create forked event when forked ([ce61a1f](https://github.com/shaojunda/ckb-explorer/commit/ce61a1f))
* implement ckb transactions index action ([26e5a36](https://github.com/shaojunda/ckb-explorer/commit/26e5a36))
* regenerate block statistic data when block forked ([c5a444e](https://github.com/shaojunda/ckb-explorer/commit/c5a444e))
* return hash_rate ([354ebf1](https://github.com/shaojunda/ckb-explorer/commit/354ebf1))
* save capacity involved to ckb_transaction ([61fc5ba](https://github.com/shaojunda/ckb-explorer/commit/61fc5ba))
* save hash rate on epoch statistic worker ([48be8e4](https://github.com/shaojunda/ckb-explorer/commit/48be8e4))
* save live_cell_changes to block ([76f3efc](https://github.com/shaojunda/ckb-explorer/commit/76f3efc))
* save live_cell_changes to ckb_transaction ([0c5589f](https://github.com/shaojunda/ckb-explorer/commit/0c5589f))
* show more attributes on dao contract ([6369a86](https://github.com/shaojunda/ckb-explorer/commit/6369a86))
* show nervos dao phase1 cells ([09ebc1b](https://github.com/shaojunda/ckb-explorer/commit/09ebc1b))
* use ckb transaction list serializer ([7d12caa](https://github.com/shaojunda/ckb-explorer/commit/7d12caa))



# [0.8.4](https://github.com/shaojunda/ckb-explorer/compare/v0.8.3...v0.8.4) (2019-12-21)


Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/blocks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def index
options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: blocks, page: @page, page_size: @page_size).call
end

render json: BlockSerializer.new(blocks, options)
render json: BlockListSerializer.new(blocks, options)
end

def show
Expand Down
21 changes: 21 additions & 0 deletions app/controllers/api/v1/ckb_transactions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ module Api
module V1
class CkbTransactionsController < ApplicationController
before_action :validate_query_params, only: :show
before_action :validate_pagination_params, :pagination_params, only: :index

def index
if from_home_page?
ckb_transactions = CkbTransaction.recent.normal.limit(ENV["HOMEPAGE_TRANSACTIONS_RECORDS_COUNT"].to_i)
render json: CkbTransactionListSerializer.new(ckb_transactions)
else
ckb_transactions = CkbTransaction.recent.normal.page(@page).per(@page_size)
options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call
render json: CkbTransactionListSerializer.new(ckb_transactions, options)
end
end

def show
ckb_transaction = CkbTransaction.cached_find(params[:id])
Expand All @@ -13,6 +25,15 @@ def show

private

def from_home_page?
params[:page].blank? || params[:page_size].blank?
end

def pagination_params
@page = params[:page] || 1
@page_size = params[:page_size] || CkbTransaction.default_per_page
end

def validate_query_params
validator = Validations::CkbTransaction.new(params)

Expand Down
5 changes: 5 additions & 0 deletions app/models/block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ def block_index_in_epoch
number - start_number
end

def fraction_epoch
OpenStruct.new(number: epoch, index: block_index_in_epoch, length: length)
end

def self.find_block!(query_key)
cached_find(query_key) || raise(Api::V1::Exceptions::BlockNotFoundError)
end
Expand Down Expand Up @@ -131,6 +135,7 @@ def invalid!
# length :decimal(30, ) default(0)
# uncles_count :integer
# compact_target :decimal(20, )
# live_cell_changes :integer
#
# Indexes
#
Expand Down
25 changes: 18 additions & 7 deletions app/models/ckb_sync/node_data_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def process_block(node_block)

ckb_transactions = build_ckb_transactions(local_block, node_block.transactions, outputs, new_dao_depositor_events)
local_block.ckb_transactions_count = ckb_transactions.size
local_block.live_cell_changes = ckb_transactions.sum(&:live_cell_changes)
CkbTransaction.import!(ckb_transactions, recursive: true, batch_size: 3500, validate: false)
input_capacities = ckb_transactions.reject(&:is_cellbase).pluck(:id).to_h { |id| [id, []] }
update_tx_fee_related_data(local_block, input_capacities)
Expand All @@ -51,7 +52,8 @@ def process_block(node_block)
def build_new_dao_depositor_events(new_dao_depositor_events)
new_dao_depositor_events.map do |address_id, tx_hash|
ckb_transaction = CkbTransaction.find_by(tx_hash: tx_hash)
ckb_transaction.dao_events.build(block: ckb_transaction.block, address_id: address_id, event_type: "new_dao_depositor", value: 1, contract_id: DaoContract.default_contract.id)
ckb_transaction.dao_events.build(block: ckb_transaction.block, address_id: address_id, event_type: "new_dao_depositor",
value: 1, contract_id: DaoContract.default_contract.id, block_timestamp: ckb_transaction.block_timestamp)
end
end

Expand Down Expand Up @@ -154,6 +156,8 @@ def invalid_block(local_tip_block)
local_tip_block.invalid!
local_tip_block.contained_addresses.each(&method(:update_address_balance_and_ckb_transactions_count))
revert_block_rewards(local_tip_block)
ForkedEvent.create!(block_number: local_tip_block.number, epoch_number: local_tip_block.epoch, block_timestamp: local_tip_block.timestamp)
Charts::BlockStatisticGenerator.new(local_tip_block.number).call

local_tip_block
end
Expand Down Expand Up @@ -337,10 +341,15 @@ def build_ckb_transaction(local_block, transaction, transaction_index)
block_timestamp: local_block.timestamp,
transaction_fee: 0,
witnesses: transaction.witnesses,
is_cellbase: transaction_index.zero?
is_cellbase: transaction_index.zero?,
live_cell_changes: live_cell_changes(transaction, transaction_index)
)
end

def live_cell_changes(transaction, transaction_index)
transaction_index.zero? ? 1 : transaction.outputs.count - transaction.inputs.count
end

def build_cell_inputs(node_inputs, ckb_transaction)
node_inputs.each do |node_input|
build_cell_input(ckb_transaction, node_input)
Expand Down Expand Up @@ -377,7 +386,8 @@ def build_cell_outputs(node_outputs, ckb_transaction, addresses, outputs_data, o
def build_deposit_dao_events(address, cell_output, ckb_transaction, new_dao_depositor_events)
if cell_output.nervos_dao_deposit?
dao_contract = DaoContract.find_or_create_by(id: 1)
ckb_transaction.dao_events.build(block: ckb_transaction.block, address_id: address.id, event_type: "deposit_to_dao", value: cell_output.capacity, contract_id: dao_contract.id)
ckb_transaction.dao_events.build(block: ckb_transaction.block, address_id: address.id, event_type: "deposit_to_dao",
value: cell_output.capacity, contract_id: dao_contract.id, block_timestamp: ckb_transaction.block_timestamp)
if address.dao_deposit.zero? && !new_dao_depositor_events.key?(address.id)
new_dao_depositor_events[address.id] = ckb_transaction.tx_hash
end
Expand All @@ -388,12 +398,12 @@ def build_withdraw_dao_events(address_id, ckb_transaction_id, local_block, previ
if previous_cell_output.nervos_dao_withdrawing?
withdraw_amount = previous_cell_output.capacity
ckb_transaction = CkbTransaction.find(ckb_transaction_id)
ckb_transaction.dao_events.create!(block: local_block, address_id: address_id, event_type: "withdraw_from_dao", value: withdraw_amount, contract_id: DaoContract.default_contract.id)
ckb_transaction.dao_events.create!(block: local_block, block_timestamp: local_block.timestamp, address_id: address_id, event_type: "withdraw_from_dao", value: withdraw_amount, contract_id: DaoContract.default_contract.id)
interest = CkbUtils.dao_interest(previous_cell_output)
ckb_transaction.dao_events.create!(block: local_block, address_id: address_id, event_type: "issue_interest", value: interest, contract_id: DaoContract.default_contract.id)
ckb_transaction.dao_events.create!(block: local_block, block_timestamp: local_block.timestamp, address_id: address_id, event_type: "issue_interest", value: interest, contract_id: DaoContract.default_contract.id)
address = Address.find(address_id)
if (address.dao_deposit - withdraw_amount).zero?
ckb_transaction.dao_events.create!(block: local_block, address_id: address_id, event_type: "take_away_all_deposit", value: 1, contract_id: DaoContract.default_contract.id)
ckb_transaction.dao_events.create!(block: local_block, block_timestamp: local_block.timestamp, address_id: address_id, event_type: "take_away_all_deposit", value: 1, contract_id: DaoContract.default_contract.id)
end
end
end
Expand Down Expand Up @@ -502,10 +512,11 @@ def calculate_tx_fee(local_block, ckb_transactions, input_capacities, outputs)
txs = []
ckb_transactions.each do |ckb_transaction|
update_transaction_fee(ckb_transaction, input_capacities[ckb_transaction.id].sum, output_capacities[ckb_transaction.id].sum)
ckb_transaction.capacity_involved = input_capacities[ckb_transaction.id].sum unless ckb_transaction.is_cellbase
txs << ckb_transaction
end

CkbTransaction.import!(txs, validate: false, on_duplicate_key_update: [:transaction_fee])
CkbTransaction.import!(txs, validate: false, on_duplicate_key_update: [:transaction_fee, :capacity_involved])
local_block.total_transaction_fee = local_block.ckb_transactions.sum(:transaction_fee)
local_block.save!
rescue ActiveRecord::RecordInvalid
Expand Down
31 changes: 17 additions & 14 deletions app/models/ckb_transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class CkbTransaction < ApplicationRecord

scope :recent, -> { order(block_timestamp: :desc) }
scope :cellbase, -> { where(is_cellbase: true) }
scope :normal, -> { where(is_cellbase: false) }

after_commit :flush_cache
before_destroy :recover_dead_cell
Expand Down Expand Up @@ -117,20 +118,22 @@ def recover_dead_cell
#
# Table name: ckb_transactions
#
# id :bigint not null, primary key
# tx_hash :binary
# deps :jsonb
# block_id :bigint
# block_number :decimal(30, )
# block_timestamp :decimal(30, )
# transaction_fee :decimal(30, )
# version :integer
# created_at :datetime not null
# updated_at :datetime not null
# is_cellbase :boolean default(FALSE)
# header_deps :binary
# cell_deps :jsonb
# witnesses :jsonb
# id :bigint not null, primary key
# tx_hash :binary
# deps :jsonb
# block_id :bigint
# block_number :decimal(30, )
# block_timestamp :decimal(30, )
# transaction_fee :decimal(30, )
# version :integer
# created_at :datetime not null
# updated_at :datetime not null
# is_cellbase :boolean default(FALSE)
# witnesses :jsonb
# header_deps :binary
# cell_deps :jsonb
# live_cell_changes :integer
# capacity_involved :decimal(30, )
#
# Indexes
#
Expand Down
8 changes: 8 additions & 0 deletions app/models/daily_statistic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ class DailyStatistic < ApplicationRecord
# created_at_unixtimestamp :integer
# created_at :datetime not null
# updated_at :datetime not null
# dao_depositors_count :string default("0")
# unclaimed_compensation :string default("0")
# claimed_compensation :string default("0")
# average_deposit_time :string default("0")
# estimated_apc :string default("0")
# mining_reward :string default("0")
# deposit_compensation :string default("0")
# treasury_amount :string default("0")
#
116 changes: 116 additions & 0 deletions app/models/dao_contract.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
class DaoContract < ApplicationRecord
validates :total_deposit, :interest_granted, :deposit_transactions_count, :withdraw_transactions_count, :depositors_count, :total_depositors_count, presence: true, numericality: { greater_than_or_equal_to: 0 }
CONTRACT_NAME = "nervos_dao".freeze
GENESIS_ISSUANCE = 336 * 10**8
ANNUAL_PRIMARY_ISSUANCE_BASE = GENESIS_ISSUANCE / 8
PRIMARY_ISSUANCE_PER_YEAR_BASE = BigDecimal(42 * 10**8)
EPOCHS_IN_ONE_NATURAL_YEAR = 2190
YEARS_IN_PERIOD = 4
EPOCHS_IN_PERIOD = BigDecimal(EPOCHS_IN_ONE_NATURAL_YEAR * YEARS_IN_PERIOD)
SECONDARY_ISSUANCE_PER_EPOCH = BigDecimal(1344 * 10**6) / 2190

def self.default_contract
find_or_create_by(id: 1)
Expand All @@ -10,6 +17,115 @@ def ckb_transactions
ckb_transaction_ids = CellOutput.nervos_dao_deposit.pluck("generated_by_id") + CellOutput.nervos_dao_withdrawing.pluck("generated_by_id") + CellOutput.nervos_dao_withdrawing.pluck("consumed_by_id").compact
CkbTransaction.where(id: ckb_transaction_ids.uniq)
end

def estimated_apc(deposit_epoch = tip_block_fraction_epoch, deposited_epochs = EPOCHS_IN_ONE_NATURAL_YEAR)
start_epoch_number = deposit_epoch.number
end_epoch_number = start_epoch_number + deposited_epochs - 1
scaled_end_epoch_number = end_epoch_number
ratio = (end_epoch_number - start_epoch_number) / EPOCHS_IN_ONE_NATURAL_YEAR
if ratio < 1
scaled_end_epoch_number = start_epoch_number + EPOCHS_IN_ONE_NATURAL_YEAR - 1
ratio = 1
end
checkpoint_start = ((start_epoch_number + 1) / EPOCHS_IN_PERIOD).ceil * EPOCHS_IN_PERIOD
checkpoint_end = ((scaled_end_epoch_number + 1) / EPOCHS_IN_PERIOD).floor * EPOCHS_IN_PERIOD
checkpoints_size = (checkpoint_end - checkpoint_start) / EPOCHS_IN_PERIOD + 1
checkpoints = checkpoints_size.to_i.times.map { |index| (index * EPOCHS_IN_PERIOD + checkpoint_start - 1).to_i }

checkpoints.unshift(start_epoch_number.to_i) if checkpoints.empty? || checkpoints[0] > start_epoch_number
checkpoints.push(scaled_end_epoch_number.to_i) if checkpoints.last < scaled_end_epoch_number
end_epoch_numbers = checkpoints[1..-1]
rates = end_epoch_numbers.each_with_index.map do |inner_end_epoch_number, index|
epoch_index = deposit_epoch.index * 1800 / deposit_epoch.length
start_epoch = OpenStruct.new(number: checkpoints[index], index: epoch_index, length: 1800)
end_epoch = OpenStruct.new(number: inner_end_epoch_number, index: epoch_index, length: 1800)
rate(start_epoch, end_epoch)
end
rate = rates.reduce(1) { |memo, rate| memo * (1 + rate) } - 1
(rate * 100) / ratio
end

def deposit_changes
total_deposit - latest_daily_statistic.total_dao_deposit.to_d
end

def depositor_changes
depositors_count - latest_daily_statistic.dao_depositors_count.to_d
end

def unclaimed_compensation_changes
latest_daily_statistic.unclaimed_compensation.to_d - penultimate_daily_statistic.unclaimed_compensation.to_d
end

def claimed_compensation_changes
latest_daily_statistic.claimed_compensation.to_d - penultimate_daily_statistic.claimed_compensation.to_d
end

def unclaimed_compensation
latest_daily_statistic.unclaimed_compensation
end

def claimed_compensation
latest_daily_statistic.claimed_compensation
end

def average_deposit_time
latest_daily_statistic.average_deposit_time
end

def mining_reward
latest_daily_statistic.mining_reward
end

def deposit_compensation
latest_daily_statistic.deposit_compensation
end

def treasury_amount
latest_daily_statistic.treasury_amount
end

private

def tip_block_fraction_epoch
Block.recent.first&.fraction_epoch || OpenStruct.new(number: 0, index: 0, length: 1800)
end

def latest_daily_statistic
@latest_daily_statistic ||= DailyStatistic.order(id: :desc).first || OpenStruct.new(total_dao_deposit: 0, dao_depositors_count: 0, unclaimed_compensation: 0, claimed_compensation: 0, average_deposit_time: 0, mining_reward: 0, deposit_compensation: 0, treasury_amount: 0)
end

def penultimate_daily_statistic
@penultimate_daily_statistic ||= DailyStatistic.order(id: :desc).second || OpenStruct.new(total_dao_deposit: 0, dao_depositors_count: 0, unclaimed_compensation: 0, claimed_compensation: 0, average_deposit_time: 0, mining_reward: 0, deposit_compensation: 0, treasury_amount: 0)
end

def alpha(start_epoch_number)
i = ((start_epoch_number + 1) / EPOCHS_IN_PERIOD).floor
p = PRIMARY_ISSUANCE_PER_YEAR_BASE / 2 ** i / 2190
p / SECONDARY_ISSUANCE_PER_EPOCH
end

def rate(start_epoch, end_epoch)
alpha = alpha(start_epoch.number)
sn = SECONDARY_ISSUANCE_PER_EPOCH * ((end_epoch.number + end_epoch.index / end_epoch.length) - (start_epoch.number + start_epoch.index / start_epoch.length))
Math.log(1 + (alpha + 1) * sn / total_issuance(start_epoch)) / (alpha + 1)
end

def total_issuance(start_epoch)
primary_issuance(start_epoch) + secondary_issuance(start_epoch)
end

def primary_issuance(start_epoch)
epochs = (start_epoch.number / EPOCHS_IN_PERIOD).floor
epochs.times.reduce(GENESIS_ISSUANCE) { |memo, item| memo + (ANNUAL_PRIMARY_ISSUANCE_BASE * YEARS_IN_PERIOD) / 2 ** item } \
+ (ANNUAL_PRIMARY_ISSUANCE_BASE * ((start_epoch.number + 1 - epochs * EPOCHS_IN_PERIOD) / EPOCHS_IN_ONE_NATURAL_YEAR)) / 2 ** epochs
end

def secondary_issuance(start_epoch)
deposit_fraction = start_epoch.number + (start_epoch.index / start_epoch.length)
epochs = deposit_fraction > 0 ? (deposit_fraction + 1) : deposit_fraction
epochs * SECONDARY_ISSUANCE_PER_EPOCH
end
end

# == Schema Information
Expand Down
Loading

0 comments on commit da8381c

Please sign in to comment.