Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 424 #1534

Merged
merged 5 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
665 changes: 548 additions & 117 deletions .rubocop.yml
100644 → 100755

Large diffs are not rendered by default.

650 changes: 0 additions & 650 deletions .rubocop_thoughtbot.yml

This file was deleted.

26 changes: 16 additions & 10 deletions app/models/statistic_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def epoch_info
{
epoch_number: tip_block.epoch.to_s,
epoch_length: tip_block.length.to_s,
index: (tip_block_number - tip_block.start_number).to_s
index: (tip_block_number - tip_block.start_number).to_s,
}
end

Expand All @@ -49,7 +49,8 @@ def current_epoch_difficulty
define_logic :transactions_count_per_minute do
interval = 100
start_block_number = [tip_block_number.to_i - interval + 1, 0].max
timestamps = Block.where(number: [start_block_number, tip_block_number]).recent.pluck(:timestamp)
timestamps = Block.where(number: [start_block_number,
tip_block_number]).recent.pluck(:timestamp)
next if timestamps.empty?

transactions_count = Block.where(number: start_block_number..tip_block_number).sum(:ckb_transactions_count)
Expand All @@ -60,7 +61,8 @@ def current_epoch_difficulty
define_logic :average_block_time do
interval = (Settings.average_block_time_interval || 100)
start_block_number = [tip_block_number.to_i - interval + 1, 0].max
timestamps = Block.where(number: [start_block_number, tip_block_number]).recent.pluck(:timestamp)
timestamps = Block.where(number: [start_block_number,
tip_block_number]).recent.pluck(:timestamp)
next if timestamps.empty?

total_block_time(timestamps) / blocks_count(interval)
Expand Down Expand Up @@ -90,14 +92,17 @@ def self.hash_rate(block_number)
define_logic :address_balance_ranking do
addresses = Address.visible.where("balance > 0").order(balance: :desc).limit(50)
addresses.each.with_index(1).map do |address, index|
{ address: address.address_hash, balance: address.balance.to_s, ranking: index.to_s }
{ address: address.address_hash, balance: address.balance.to_s,
ranking: index.to_s }
end
end

define_logic :blockchain_info do
message_need_to_be_filtered_out = "CKB v0.105.* have bugs. Please upgrade to the latest version."
result = CkbSync::Api.instance.get_blockchain_info
result.alerts.delete_if { |alert| alert.message == message_need_to_be_filtered_out }
result.alerts.delete_if do |alert|
alert.message == message_need_to_be_filtered_out
end
JSON.generate(result.as_json)
end

Expand All @@ -108,10 +113,10 @@ def self.hash_rate(block_number)
pluck(:id, :created_at, :transaction_fee, :bytes, :confirmation_time)
txs.map do |id, created_at, transaction_fee, bytes, confirmation_time|
{
id: id,
id:,
timestamp: created_at.to_i,
fee_rate: (transaction_fee.to_f / bytes),
confirmation_time: confirmation_time
confirmation_time:,
}
end
end
Expand All @@ -123,7 +128,7 @@ def self.hash_rate(block_number)
order("id desc").limit(100)

# This is a patch for those pending tx which has no `bytes`
fee_rates = fee_rates.map { |tx|
fee_rates = fee_rates.map do |tx|
tx_bytes = 0
if tx.bytes.blank? || tx.bytes == 0
Rails.logger.info "== checking tx bytes: #{tx.tx_hash}, #{tx.id}"
Expand All @@ -137,12 +142,12 @@ def self.hash_rate(block_number)
end

tx
}.select { |e| e.bytes > 0 }
end.select { |e| e.bytes > 0 }

fee_rates.map do |tx|
{
id: tx.id,
fee_rate: (tx.transaction_fee.to_f / tx.bytes)
fee_rate: (tx.transaction_fee.to_f / tx.bytes),
}
end
end
Expand Down Expand Up @@ -210,4 +215,5 @@ def tip_block
# updated_at :datetime not null
# pending_transaction_fee_rates :jsonb
# transaction_fee_rates :jsonb
# ckb_hodl_waves :jsonb
#
4 changes: 4 additions & 0 deletions app/serializers/statistic_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ class StatisticSerializer
attribute :maintenance_info, if: Proc.new { |_record, params|
params && params[:info_name] == "maintenance_info"
}

attribute :ckb_hodl_waves, if: Proc.new { |_record, params|
params && params[:info_name] == "ckb_hodl_waves"
}
end
49 changes: 49 additions & 0 deletions app/workers/charts/ckb_hodl_waves_statistic.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module Charts
class CkbbHodlWavesStatistic
include Sidekiq::Worker
sidekiq_options queue: "critical"

def perform
over_three_years = CellOutput.live.generated_before(3.years.ago.to_i * 1000).sum(:capacity)
one_year_to_three_years = CellOutput.live.generated_between(
3.years.ago.to_i * 1000, 1.year.ago.to_i * 1000
).sum(:capacity)
six_months_to_one_year = CellOutput.live.generated_between(
1.year.ago.to_i * 1000, 6.months.ago.to_i * 1000
).sum(:capacity)
three_months_to_six_months = CellOutput.live.generated_between(
6.months.ago.to_i * 1000, 3.months.ago.to_i * 1000
).sum(:capacity)
one_month_to_three_months = CellOutput.live.generated_between(
3.months.ago.to_i * 1000, 1.month.ago.to_i * 1000
).sum(:capacity)
one_week_to_one_month = CellOutput.live.generated_between(
1.month.ago.to_i * 1000, 1.week.ago.to_i * 1000
).sum(:capacity)
day_to_one_week = CellOutput.live.generated_between(
1.week.ago.to_i * 1000, 1.day.ago.to_i * 1000
).sum(:capacity)
latest_day = CellOutput.live.generated_between(
1.day.ago.beginning_of_day.to_i * 1000, 1.day.ago.end_of_day.to_i * 1000
).sum(:capacity)

info = {
total_supply: MarketData.new.indicators_json["total_supply"],
updated_at: Time.current.to_i,
}

ckb = {
over_three_years:,
one_year_to_three_years:,
six_months_to_one_year:,
three_months_to_six_months:,
one_month_to_three_months:,
one_week_to_one_month:,
day_to_one_week:,
latest_day:,
}.transform_values { |value| (value / 10**8).truncate(8) }

StatisticInfo.first.update(capacity_hodl_waves: ckb.merge!(info))
zmcNotafraid marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddCkbHodlWavesToStatisticInfo < ActiveRecord::Migration[7.0]
def change
add_column :statistic_infos, :ckb_hodl_waves, :jsonb
end
end
6 changes: 4 additions & 2 deletions db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,8 @@ CREATE TABLE public.statistic_infos (
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
pending_transaction_fee_rates jsonb,
transaction_fee_rates jsonb
transaction_fee_rates jsonb,
ckb_hodl_waves jsonb
);


Expand Down Expand Up @@ -4750,6 +4751,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20230913091025'),
('20230914120928'),
('20230918033957'),
('20231017074221');
('20231017074221'),
('20231218082938');


8 changes: 6 additions & 2 deletions lib/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def call_worker(clz)
call_worker Charts::DailyStatistic
end

s.cron "10 8 * * *" do
call_worker Charts::CkbHodlWavesStatistic
end

s.every "10m", overlap: false do
call_worker Charts::BlockStatistic
end
Expand Down Expand Up @@ -95,7 +99,8 @@ def call_worker(clz)

s.every "4h", overlap: false do
puts "reset address_balance_ranking, miner_ranking, last_n_days_transaction_fee_rates"
StatisticInfo.default.reset! :address_balance_ranking, :miner_ranking, :last_n_days_transaction_fee_rates
StatisticInfo.default.reset! :address_balance_ranking, :miner_ranking,
:last_n_days_transaction_fee_rates
end

s.every "1h", overlap: false do
Expand All @@ -112,7 +117,6 @@ def call_worker(clz)
call_worker Charts::ForkedEventProcessor
end

# run at every mondy 06:00
s.cron "0 6 * * 1" do
call_worker CleanAddressBlockSnapshotWorker
end
Expand Down
74 changes: 53 additions & 21 deletions test/controllers/api/v1/statistics_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
setup do
CkbSync::Api.any_instance.stubs(:get_tip_block_number).returns(100)
CkbSync::Api.any_instance.stubs(:get_blockchain_info).returns(
OpenStruct.new(alerts: OpenStruct.new(message: "test"))
OpenStruct.new(alerts: OpenStruct.new(message: "test")),
)
CkbSync::Api.any_instance.stubs(:get_current_epoch).returns(
CKB::Types::Epoch.new(
compact_target: "0x1000",
length: "0x07d0",
number: "0x0",
start_number: "0x0"
)
start_number: "0x0",
),
)
# StatisticInfo.any_instance.stubs(:id).returns(1)
end
Expand Down Expand Up @@ -43,24 +43,30 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest

test "should respond with error object when Content-Type is wrong" do
error_object = Api::V1::Exceptions::InvalidContentTypeError.new
response_json = RequestErrorSerializer.new([error_object], message: error_object.title).serialized_json
response_json = RequestErrorSerializer.new([error_object],
message: error_object.title).serialized_json

get api_v1_statistics_url, headers: { "Content-Type": "text/plain" }

assert_equal response_json, response.body
end

test "should respond with 406 Not Acceptable when Accept is wrong" do
get api_v1_statistics_url, headers: { "Content-Type": "application/vnd.api+json", "Accept": "application/json" }
get api_v1_statistics_url,
headers: { "Content-Type": "application/vnd.api+json",
"Accept": "application/json" }

assert_equal 406, response.status
end

test "should respond with error object when Accept is wrong" do
error_object = Api::V1::Exceptions::InvalidAcceptError.new
response_json = RequestErrorSerializer.new([error_object], message: error_object.title).serialized_json
response_json = RequestErrorSerializer.new([error_object],
message: error_object.title).serialized_json

get api_v1_statistics_url, headers: { "Content-Type": "application/vnd.api+json", "Accept": "application/json" }
get api_v1_statistics_url,
headers: { "Content-Type": "application/vnd.api+json",
"Accept": "application/json" }

assert_equal response_json, response.body
end
Expand All @@ -80,7 +86,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
statistic_info.reset_all!
valid_get api_v1_statistics_url

assert_equal IndexStatisticSerializer.new(statistic_info).serialized_json, response.body
assert_equal IndexStatisticSerializer.new(statistic_info).serialized_json,
response.body
end

test "should get success code when call show" do
Expand All @@ -99,8 +106,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
compact_target: "0x1000",
length: "0x07d0",
number: "0x0",
start_number: "0x0"
)
start_number: "0x0",
),
)
generate_miner_ranking_related_data
StatisticInfo.default.reset_all!
Expand All @@ -116,8 +123,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
compact_target: "0x1000",
length: "0x07d0",
number: "0x0",
start_number: "0x0"
)
start_number: "0x0",
),
)
generate_miner_ranking_related_data
statistic_info = StatisticInfo.default
Expand All @@ -134,8 +141,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
compact_target: "0x1000",
length: "0x07d0",
number: "0x0",
start_number: "0x0"
)
start_number: "0x0",
),
)
generate_miner_ranking_related_data(1550578400000)
StatisticInfo.default.reset! :miner_ranking
Expand Down Expand Up @@ -164,7 +171,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest

valid_get api_v1_statistic_url("tip_block_number")

assert_equal tip_block_number, json.dig("data", "attributes", "tip_block_number")
assert_equal tip_block_number,
json.dig("data", "attributes", "tip_block_number")
end

test "should return average block time when param is average_block_time" do
Expand All @@ -174,7 +182,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest

valid_get api_v1_statistic_url("average_block_time")

assert_equal average_block_time, json.dig("data", "attributes", "average_block_time")
assert_equal average_block_time,
json.dig("data", "attributes", "average_block_time")
end

test "should return current epoch difficulty when param is current_epoch_difficulty" do
Expand All @@ -183,7 +192,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest

valid_get api_v1_statistic_url("current_epoch_difficulty")

assert_equal current_epoch_difficulty, json.dig("data", "attributes", "current_epoch_difficulty")
assert_equal current_epoch_difficulty,
json.dig("data", "attributes", "current_epoch_difficulty")
end

test "should return current hash rate when param is hash_rate" do
Expand All @@ -202,7 +212,7 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
difficulty: "0x100",
median_time: "0x16bd6605e65",
chain: "ckb_testnet",
alerts: []
alerts: [],
)
CkbSync::Api.any_instance.stubs(:get_blockchain_info).returns(blockchain_info)
statistic_info = StatisticInfo.default
Expand All @@ -211,7 +221,8 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest

assert_equal StatisticSerializer.new(statistic_info, { params: { info_name: "blockchain_info" } }).serialized_json,
response.body
assert_equal blockchain_info.as_json, json.dig("data", "attributes", "blockchain_info")
assert_equal blockchain_info.as_json,
json.dig("data", "attributes", "blockchain_info")
end

test "should return top 50 addresses balance list when param is address balance ranking" do
Expand All @@ -222,19 +233,40 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest
statistic_info.reset! :address_balance_ranking
valid_get api_v1_statistic_url("address_balance_ranking")
assert_equal %w(ranking address balance).sort,
json.dig("data", "attributes", "address_balance_ranking").map(&:keys).uniq.flatten.sort
json.dig("data", "attributes",
"address_balance_ranking").map(&:keys).uniq.flatten.sort
assert_equal StatisticSerializer.new(statistic_info, { params: { info_name: "address_balance_ranking" } }).serialized_json,
response.body
end

test "should respond with error object when statistic info name is invalid" do
error_object = Api::V1::Exceptions::StatisticInfoNameInvalidError.new
response_json = RequestErrorSerializer.new([error_object], message: error_object.title).serialized_json
response_json = RequestErrorSerializer.new([error_object],
message: error_object.title).serialized_json

valid_get api_v1_statistic_url("hash_rates")

assert_equal response_json, response.body
end

test "should return current ckb_hodl_waves when param is ckb_hodl_waves" do
ckb_hodl_waves = { "over_three_years" => 19531171649.691193,
"one_year_to_three_years" => 23338346194.19826,
"six_months_to_one_year" => 19609620799.532352,
"three_months_to_six_months" => 2236264635.3570275,
"one_month_to_three_months" => 814754775.4523662,
"one_week_to_one_month" => 456541010.49045384,
"day_to_one_week" => 104631888.5063308,
"latest_day" => 22211617.27774267,
"total_supply" => 40845092357.49983,
"updated_at" => 1702895323 }
create(:statistic_info, ckb_hodl_waves:)

valid_get api_v1_statistic_url("ckb_hodl_waves")

assert_equal ckb_hodl_waves,
json.dig("data", "attributes", "ckb_hodl_waves")
end
end
end
end
Loading