diff --git a/app/controllers/concerns/cell_data_comparator.rb b/app/controllers/concerns/cell_data_comparator.rb index 860840d32..3f259d8f7 100644 --- a/app/controllers/concerns/cell_data_comparator.rb +++ b/app/controllers/concerns/cell_data_comparator.rb @@ -10,10 +10,10 @@ def compare_cells(transaction) udt_transfers = diff_udt_cells(inputs, outputs) cota_nft_transfers = diff_cota_nft_cells(transaction, inputs, outputs) normal_nft_transfers = diff_normal_nft_cells(inputs, outputs) - nft_class_transfers = diff_nft_capacities(inputs, outputs) + dao_transfers = diff_dao_capacities(inputs, outputs) merged_transfers = - [normal_transfers, udt_transfers, cota_nft_transfers, normal_nft_transfers, nft_class_transfers].reduce do |acc, h| + [normal_transfers, udt_transfers, cota_nft_transfers, normal_nft_transfers, dao_transfers].reduce do |acc, h| acc.merge(h) { |_, ov, nv| ov + nv } end @@ -73,6 +73,20 @@ def diff_udt_cells(inputs, outputs) transfers end + def diff_dao_capacities(inputs, outputs) + transfers = Hash.new { |h, k| h[k] = Array.new } + cell_types = %w(nervos_dao_deposit nervos_dao_withdrawing) + inputs = inputs.where(cell_type: cell_types).group(:address_id, :cell_type).sum(:capacity) + outputs = outputs.where(cell_type: cell_types).group(:address_id, :cell_type).sum(:capacity) + + (inputs.keys | outputs.keys).each do |k| + capacity = outputs[k].to_f - inputs[k].to_f + transfers[k[0]] << CkbUtils.hash_value_to_s({ capacity:, cell_type: k[1] }) + end + + transfers + end + def diff_cota_nft_cells(transaction, inputs, outputs) transfers = Hash.new { |h, k| h[k] = Array.new } inputs = inputs.cota_regular.group(:address_id).sum(:capacity) @@ -88,58 +102,78 @@ def diff_cota_nft_cells(transaction, inputs, outputs) def diff_normal_nft_cells(inputs, outputs) transfers = Hash.new { |h, k| h[k] = Array.new } - cell_types = %w(m_nft_token nrc_721_token spore_cell) + nft_infos = Hash.new { |h, k| h[k] = nil } + cell_types = %w(m_nft_token nrc_721_token spore_cell m_nft_issuer m_nft_class nrc_721_factory cota_registry spore_cluster) - process_nft = ->(c, h) { + process_nft = ->(c, h, o) { k = [c.address_id, c.cell_type, c.type_hash] h[k] ||= { capacity: 0.0, count: 0 } h[k][:capacity] += c.capacity - h[k][:count] -= 1 + h[k][:count] += o + + unless nft_infos[c.type_hash] + nft_infos[c.type_hash] = nft_info(c) + end } - inputs = inputs.where(cell_type: cell_types).each_with_object({}) { |c, h| process_nft.call(c, h) } - outputs = outputs.where(cell_type: cell_types).each_with_object({}) { |c, h| process_nft.call(c, h) } + inputs = inputs.where(cell_type: cell_types).each_with_object({}) { |c, h| process_nft.call(c, h, -1) } + outputs = outputs.where(cell_type: cell_types).each_with_object({}) { |c, h| process_nft.call(c, h, 1) } (inputs.keys | outputs.keys).each do |k| address_id, cell_type, type_hash = k - token_id, collection_name = token_info(type_hash) input = inputs[k] output = outputs[k] capacity = output&.dig(:capacity).to_f - input&.dig(:capacity).to_f count = output&.dig(:count).to_i + input&.dig(:count).to_i - transfers[address_id] << CkbUtils.hash_value_to_s({ capacity:, cell_type:, token_id:, collection_name:, count: }) + transfer = { capacity:, cell_type:, count: } + transfer.merge!(nft_infos[type_hash]) if nft_infos[type_hash] + transfers[address_id] << CkbUtils.hash_value_to_s(transfer) end transfers end - def diff_nft_capacities(inputs, outputs) - transfers = Hash.new { |h, k| h[k] = Array.new } - cell_types = %w(m_nft_issuer m_nft_class nrc_721_factory cota_registry spore_cluster) - inputs = inputs.where(cell_type: cell_types).group(:address_id, :cell_type).sum(:capacity) - outputs = outputs.where(cell_type: cell_types).group(:address_id, :cell_type).sum(:capacity) - - (inputs.keys | outputs.keys).each do |k| - capacity = outputs[k].to_f - inputs[k].to_f - transfers[k[0]] << CkbUtils.hash_value_to_s({ capacity:, cell_type: k[1] }) + def nft_info(cell) + case cell.cell_type + when "m_nft_token", "nrc_721_token", "spore_cell" + item = TokenItem.joins(:type_script).where(type_script: { script_hash: cell.type_hash }).take + { toekn_id: item&.token_id, name: item&.collection&.name } + when "m_nft_issuer" + { name: CkbUtils.parse_issuer_data(cell.data).info["name"] } + when "m_nft_class" + { name: CkbUtils.parse_token_class_data(cell.data).name } + when "nrc_721_factory" + type_script = cell.type_script + factory_cell = NrcFactoryCell.find_by( + code_hash: type_script.code_hash, + hash_type: type_script.hash_type, + args: type_script.args, + verified: true, + ) + { name: factory_cell&.name } + when "spore_cluster" + { name: CkbUtils.parse_spore_cluster_data(cell.data)[:name] } + when "omiga_inscription_info" + type_script = cell.type_script + info = OmigaInscriptionInfo.find_by( + code_hash: type_script.code_hash, + hash_type: type_script.hash_type, + args: type_script.args, + ) + { name: info&.name } end - - transfers - end - - def token_info(script_hash) - item = TokenItem.joins(:type_script).where(type_script: { script_hash: }).take - [item&.token_id, item&.collection&.name] end def cota_info(transaction, address_id) info = Array.new process_transfer = ->(item, count) { collection = item.collection - info << CkbUtils.hash_value_to_s({ - collection_name: collection.name, - count:, - token_id: item.token_id, - }) + info << CkbUtils.hash_value_to_s( + { + name: collection.name, + count:, + token_id: item.token_id, + }, + ) } transaction.token_transfers.each do |t| diff --git a/app/jobs/import_transaction_job.rb b/app/jobs/import_transaction_job.rb index 1316d64a2..a85d5c2a1 100644 --- a/app/jobs/import_transaction_job.rb +++ b/app/jobs/import_transaction_job.rb @@ -14,8 +14,7 @@ def perform(tx_hash, extra_data = {}) CkbTransaction.write_raw_hash_cache tx_hash["hash"], tx_hash tx_hash = tx_hash["hash"] end - # raw = CkbTransaction.fetch_raw_hash(tx_hash) - @tx = CkbTransaction.unscoped.create_with(tx_status: :pending).find_or_create_by! tx_hash: tx_hash + @tx = CkbTransaction.unscoped.create_with(tx_status: :pending).find_or_create_by!(tx_hash:) return unless tx.tx_pending? Rails.logger.info "Importing #{tx.tx_hash}" @@ -49,23 +48,23 @@ def perform(tx_hash, extra_data = {}) sdk_tx.inputs.each_with_index do |input, index| if input.previous_output.tx_hash == CellOutput::SYSTEM_TX_HASH tx.cell_inputs.create_with( - index: index + index:, ).create_or_find_by( previous_cell_output_id: nil, - from_cell_base: true + from_cell_base: true, ) else cell = CellOutput.find_by( tx_hash: input.previous_output.tx_hash, - cell_index: input.previous_output.index + cell_index: input.previous_output.index, ) if cell process_input tx.cell_inputs.create_with( - previous_cell_output_id: cell.id + previous_cell_output_id: cell.id, ).create_or_find_by!( ckb_transaction_id: txid, - index: index + index:, ) process_deployed_cell(cell.lock_script) process_deployed_cell(cell.type_script) if cell.type_script @@ -74,7 +73,8 @@ def perform(tx_hash, extra_data = {}) tx.cell_inputs.create_or_find_by!( previous_tx_hash: input.previous_output.tx_hash, previous_index: input.previous_output.index, - since: input.since + since: input.since, + index:, ) puts "Missing input #{input.previous_output.to_h} in #{tx_hash}" # cannot find corresponding cell output, @@ -93,22 +93,24 @@ def perform(tx_hash, extra_data = {}) lock = LockScript.process(output.lock) t = TypeScript.process(output.type) if output.type cell = tx.cell_outputs.find_or_create_by( - tx_hash: tx_hash, - cell_index: index + tx_hash:, + cell_index: index, ) + + # after the cell is created, create a datum + if output_data.present? && output_data != "0x" + (cell.cell_datum || cell.build_cell_datum).update(data: [output_data[2..]].pack("H*")) + end + cell.lock_script = lock cell.type_script = t cell.update!( address_id: lock.address_id, capacity: output.capacity, occupied_capacity: cell.calculate_min_capacity, - status: "pending" + status: "pending", ) puts "output cell created tx_hash: #{tx_hash}, index: #{index}, cell_id: #{cell.id}" - # after the cell is created, create a datum - if output_data.present? && output_data != "0x" - (cell.cell_datum || cell.build_cell_datum).update(data: [output_data[2..]].pack("H*")) - end process_output cell process_deployed_cell(cell.lock_script) @@ -139,7 +141,7 @@ def parse_code_dep(cell_dep) ckb_transaction_id: ckb_transaction.id, # check if we already known the relationship between the contract cell and contract contract_id: DeployedCell.cell_output_id_to_contract_id(cell_output.id), - implicit: cell_dep["implicit"] || false + implicit: cell_dep["implicit"] || false, } # we don't know how the cells in transaction may refer to the contract cell @@ -165,13 +167,13 @@ def save_relationship if cell_dependencies_attrs.present? CellDependency.upsert_all cell_dependencies_attrs.uniq { |a| a[:contract_cell_id] - }, unique_by: [:ckb_transaction_id, :contract_cell_id] + }, unique_by: %i[ckb_transaction_id contract_cell_id] end DeployedCell.upsert_all deployed_cells_attrs, unique_by: [:cell_output_id] if deployed_cells_attrs.present? deployed_cells_attrs.each do |deployed_cell_attr| DeployedCell.write_cell_output_id_to_contract_id( deployed_cell_attr[:cell_output_id], - deployed_cell_attr[:contract_id] + deployed_cell_attr[:contract_id], ) end end @@ -184,10 +186,10 @@ def process_deployed_cell(lock_script_or_type_script) dep = case lock_script_or_type_script.hash_type - when "data" - by_data_hash[lock_script_or_type_script.code_hash] - when "type" - by_type_hash[lock_script_or_type_script.code_hash] + when "data" + by_data_hash[lock_script_or_type_script.code_hash] + when "type" + by_type_hash[lock_script_or_type_script.code_hash] end return unless dep @@ -204,7 +206,7 @@ def process_deployed_cell(lock_script_or_type_script) deployed_cells_attrs << { contract_id: contract.id, - cell_output_id: dep[:contract_cell_id] + cell_output_id: dep[:contract_cell_id], } end end @@ -229,7 +231,7 @@ def process_cell_dep(cell_dep) dep_type: cell_dep["dep_type"], ckb_transaction_id: ckb_transaction.id, contract_id: nil, - implicit: false + implicit: false, } binary_data = mid_cell.binary_data # binary_data = [hex_data[2..-1]].pack("H*") @@ -244,10 +246,10 @@ def process_cell_dep(cell_dep) parse_code_dep( "out_point" => { "tx_hash" => "0x#{tx_hash}", - "index" => cell_index + "index" => cell_index, }, "dep_type" => "code", - "implicit" => true # this is an implicit dependency + "implicit" => true, # this is an implicit dependency ) end end @@ -258,8 +260,8 @@ def process_header_deps sdk_tx.header_deps.each_with_index do |header_dep, index| header_deps_attrs << { ckb_transaction_id: txid, - index: index, - header_hash: header_dep + index:, + header_hash: header_dep, } end if header_deps_attrs.present? @@ -274,7 +276,7 @@ def process_witnesses witnesses_attrs << { ckb_transaction_id: txid, index: i, - data: w + data: w, } end if witnesses_attrs.present? @@ -291,7 +293,7 @@ def process_input(cell_input) changes = address_changes[address_id] ||= { balance: 0, - balance_occupied: 0 + balance_occupied: 0, } changes[:balance] -= cell_output.capacity changes[:balance_occupied] -= cell_output.occupied_capacity if cell_output.occupied_capacity @@ -304,7 +306,7 @@ def process_output(cell_output) changes = address_changes[address_id] ||= { balance: 0, - balance_occupied: 0 + balance_occupied: 0, } changes[:balance] += cell_output.capacity changes[:balance_occupied] += cell_output.occupied_capacity @@ -315,19 +317,19 @@ def save_changes attrs = address_changes.map do |address_id, c| { - address_id: address_id, + address_id:, ckb_transaction_id: txid, - changes: c + changes: c, } end TransactionAddressChange.upsert_all( attrs, - unique_by: [:address_id, :ckb_transaction_id], + unique_by: %i[address_id ckb_transaction_id], on_duplicate: Arel.sql( - "changes = transaction_address_changes.changes || excluded.changes" - ) + "changes = transaction_address_changes.changes || excluded.changes", + ), ) - AccountBook.upsert_all address_changes.keys.map{|address_id| {ckb_transaction_id: tx.id, address_id:}} + AccountBook.upsert_all address_changes.keys.map { |address_id| { ckb_transaction_id: tx.id, address_id: } } end end end diff --git a/app/models/cell_output.rb b/app/models/cell_output.rb index 691763982..25025f98e 100644 --- a/app/models/cell_output.rb +++ b/app/models/cell_output.rb @@ -250,7 +250,7 @@ def nrc_721_nft_info type_hash:, published: factory_cell&.verified, display_name: factory_cell&.name, - nan: "", + uan: "", } when "nrc_721_token" udt = Udt.find_by(type_hash:) diff --git a/app/models/daily_statistic.rb b/app/models/daily_statistic.rb index d1aceed13..453058288 100644 --- a/app/models/daily_statistic.rb +++ b/app/models/daily_statistic.rb @@ -5,10 +5,11 @@ class DailyStatistic < ApplicationRecord transactions_count addresses_count total_dao_deposit live_cells_count dead_cells_count avg_hash_rate avg_difficulty uncle_rate total_depositors_count address_balance_distribution total_tx_fee occupied_capacity daily_dao_deposit daily_dao_depositors_count circulation_ratio daily_dao_withdraw nodes_count circulating_supply burnt locked_capacity treasury_amount mining_reward - deposit_compensation liquidity created_at_unixtimestamp + deposit_compensation liquidity created_at_unixtimestamp ckb_hodl_wave ).freeze MILLISECONDS_IN_DAY = BigDecimal(24 * 60 * 60 * 1000) GENESIS_TIMESTAMP = 1573852190812 + TESTNET_GENESIS_TIMESTAMP = 1589276230000 attr_accessor :from_scratch @@ -260,9 +261,9 @@ def liquidity millisecond_start = range[0] * 1000 millisecond_end = range[1] * 1000 block_count = Block.where( - number: start_block_number..tip_block_number + number: start_block_number..tip_block_number, ).where( - block_time: (millisecond_start + 1)..millisecond_end + block_time: (millisecond_start + 1)..millisecond_end, ).count [range[1], block_count] end @@ -271,7 +272,7 @@ def liquidity define_logic :epoch_time_distribution do max_n = 119 ranges = [[0, 180]] + (180..(180 + max_n)).map { |n| [n, n + 1] } - ranges.each_with_index.map { |range, index| + ranges.each_with_index.map do |range, index| milliseconds_start = range[0] * 60 * 1000 milliseconds_end = range[1] * 60 * 1000 condition = @@ -285,7 +286,7 @@ def liquidity epoch_count = ::EpochStatistic.where(epoch_time: condition).count [range[1], epoch_count] - }.compact + end.compact end define_logic :epoch_length_distribution do @@ -295,15 +296,15 @@ def liquidity interval = 499 start_epoch_number = [0, tip_epoch_number - interval].max - ranges.each_with_index.map { |range, _index| + ranges.each_with_index.map do |range, _index| epoch_count = ::EpochStatistic.where( - epoch_number: start_epoch_number..tip_epoch_number + epoch_number: start_epoch_number..tip_epoch_number, ).where( - epoch_length: (range[0] + 1)..range[1] + epoch_length: (range[0] + 1)..range[1], ).count [range[1], epoch_count] - }.compact + end.compact end define_logic :locked_capacity do @@ -316,6 +317,90 @@ def liquidity market_data.bug_bounty_locked end + define_logic :ckb_hodl_wave do + over_three_years = + if time_range_exceeded?(3.years) + CellOutput.live.generated_before(to_be_counted_date.years_ago(3).to_i * 1000).sum(:capacity) + + CellOutput.dead.generated_before(to_be_counted_date.years_ago(3).to_i * 1000).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + else + 0 + end + + one_year_to_three_years = + if time_range_exceeded?(1.year) + CellOutput.live.generated_between( + to_be_counted_date.years_ago(3).to_i * 1000, to_be_counted_date.years_ago(1).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.years_ago(3).to_i * 1000, to_be_counted_date.years_ago(1).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + else + 0 + end + + six_months_to_one_year = + if time_range_exceeded?(6.months) + CellOutput.live.generated_between( + to_be_counted_date.years_ago(1).to_i * 1000, to_be_counted_date.months_ago(6).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.years_ago(1).to_i * 1000, to_be_counted_date.months_ago(6).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + else + 0 + end + + three_months_to_six_months = + if time_range_exceeded?(3.months) + CellOutput.live.generated_between( + to_be_counted_date.months_ago(6).to_i * 1000, to_be_counted_date.months_ago(3).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.months_ago(6).to_i * 1000, to_be_counted_date.months_ago(3).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + else + 0 + end + + one_month_to_three_months = + if time_range_exceeded?(1.month) + CellOutput.live.generated_between( + to_be_counted_date.months_ago(3).to_i * 1000, to_be_counted_date.months_ago(1).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.months_ago(3).to_i * 1000, to_be_counted_date.months_ago(1).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + else + 0 + end + + one_week_to_one_month = CellOutput.live.generated_between( + to_be_counted_date.months_ago(1).to_i * 1000, to_be_counted_date.weeks_ago(1).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.months_ago(1).to_i * 1000, to_be_counted_date.weeks_ago(1).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + + day_to_one_week = CellOutput.live.generated_between( + to_be_counted_date.weeks_ago(1).to_i * 1000, to_be_counted_date.days_ago(1).to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.weeks_ago(1).to_i * 1000, to_be_counted_date.days_ago(1).to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + + latest_day = CellOutput.live.generated_between( + to_be_counted_date.days_ago(1).to_i * 1000, to_be_counted_date.to_i * 1000 + ).sum(:capacity) + CellOutput.dead.generated_between( + to_be_counted_date.days_ago(1).to_i * 1000, to_be_counted_date.to_i * 1000 + ).consumed_after(to_be_counted_date.to_i * 1000).sum(:capacity) + + { + 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:, + total_supply: total_supply + burnt, + }.transform_values { |value| (value.to_f / 10**8).truncate(8) } + end + private def to_be_counted_date @@ -339,6 +424,14 @@ def current_tip_block end end + def time_range_exceeded?(time_range) + if CkbSync::Api.instance.mode == CKB::MODE::MAINNET + GENESIS_TIMESTAMP + time_range.to_i * 1000 <= started_at + else + TESTNET_GENESIS_TIMESTAMP + time_range.to_i * 1000 <= started_at + end + end + def phase1_dao_interests @phase1_dao_interests ||= begin @@ -467,6 +560,7 @@ def aggron_first_day? # nodes_distribution :jsonb # nodes_count :integer # locked_capacity :decimal(30, ) +# ckb_hodl_wave :jsonb # # Indexes # diff --git a/app/models/statistic_info.rb b/app/models/statistic_info.rb index fb6ea5054..83842bfa5 100644 --- a/app/models/statistic_info.rb +++ b/app/models/statistic_info.rb @@ -215,5 +215,4 @@ def tip_block # updated_at :datetime not null # pending_transaction_fee_rates :jsonb # transaction_fee_rates :jsonb -# ckb_hodl_waves :jsonb # diff --git a/app/serializers/cell_output_serializer.rb b/app/serializers/cell_output_serializer.rb index 27e22829f..e0a2ade05 100644 --- a/app/serializers/cell_output_serializer.rb +++ b/app/serializers/cell_output_serializer.rb @@ -3,6 +3,10 @@ class CellOutputSerializer attributes :cell_type, :tx_hash, :cell_index, :type_hash, :data + attribute :block_number do |object| + object.block.number.to_s + end + attribute :capacity do |object| object.capacity.to_s end @@ -25,18 +29,20 @@ class CellOutputSerializer attribute :extra_info do |object| case object.cell_type + when "normal" + { type: "ckb", capacity: object.capacity.to_s } when "udt" - object.udt_info + object.udt_info.merge!(type: "udt") when "cota_registry" - object.cota_registry_info + object.cota_registry_info.merge!(type: "cota") when "cota_regular" - object.cota_regular_info + object.cota_regular_info.merge!(type: "cota") when "m_nft_issuer", "m_nft_class", "m_nft_token" - object.m_nft_info + object.m_nft_info.merge!(type: "m_nft") when "nrc_721_token", "nrc_721_factory" - object.nrc_721_nft_info + object.nrc_721_nft_info.merge!(type: "nrc_721") when "omiga_inscription_info", "omiga_inscription" - object.omiga_inscription_info + object.omiga_inscription_info.merge!(type: "omiga_inscription") end end end diff --git a/app/serializers/daily_statistic_serializer.rb b/app/serializers/daily_statistic_serializer.rb index c43e6d898..675012aa9 100644 --- a/app/serializers/daily_statistic_serializer.rb +++ b/app/serializers/daily_statistic_serializer.rb @@ -124,4 +124,8 @@ class DailyStatisticSerializer } do |object| object.liquidity.to_s end + + attribute :ckb_hodl_wave, if: Proc.new { |_record, params| + params.present? && params[:indicator].include?("ckb_hodl_wave") + } end diff --git a/app/serializers/statistic_serializer.rb b/app/serializers/statistic_serializer.rb index c3c9c7f47..409723813 100644 --- a/app/serializers/statistic_serializer.rb +++ b/app/serializers/statistic_serializer.rb @@ -38,8 +38,4 @@ 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 diff --git a/app/services/charts/daily_statistic_generator.rb b/app/services/charts/daily_statistic_generator.rb index 2d5947a0d..48d6e04bb 100644 --- a/app/services/charts/daily_statistic_generator.rb +++ b/app/services/charts/daily_statistic_generator.rb @@ -36,7 +36,7 @@ def updated_attrs treasury_amount estimated_apc live_cells_count dead_cells_count avg_hash_rate avg_difficulty uncle_rate address_balance_distribution total_tx_fee occupied_capacity daily_dao_deposit total_supply block_time_distribution - epoch_time_distribution epoch_length_distribution locked_capacity + epoch_time_distribution epoch_length_distribution locked_capacity ckb_hodl_wave } established_order + others diff --git a/app/workers/charts/ckb_hodl_waves_statistic.rb b/app/workers/charts/ckb_hodl_waves_statistic.rb deleted file mode 100644 index 19b8d324f..000000000 --- a/app/workers/charts/ckb_hodl_waves_statistic.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Charts - class CkbHodlWavesStatistic - 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(ckb_hodl_waves: ckb.merge!(info)) - end - end -end diff --git a/db/migrate/20240205023511_remove_ckb_hodl_waves_to_statistic_info.rb b/db/migrate/20240205023511_remove_ckb_hodl_waves_to_statistic_info.rb new file mode 100644 index 000000000..61921d231 --- /dev/null +++ b/db/migrate/20240205023511_remove_ckb_hodl_waves_to_statistic_info.rb @@ -0,0 +1,5 @@ +class RemoveCkbHodlWavesToStatisticInfo < ActiveRecord::Migration[7.0] + def change + remove_columns :statistic_infos, :ckb_hodl_waves + end +end diff --git a/db/migrate/20240205024238_add_ckb_hodl_waves_to_daily_statistic.rb b/db/migrate/20240205024238_add_ckb_hodl_waves_to_daily_statistic.rb new file mode 100644 index 000000000..737a5e3ab --- /dev/null +++ b/db/migrate/20240205024238_add_ckb_hodl_waves_to_daily_statistic.rb @@ -0,0 +1,5 @@ +class AddCkbHodlWavesToDailyStatistic < ActiveRecord::Migration[7.0] + def change + add_column :daily_statistics, :ckb_hodl_wave, :jsonb + end +end diff --git a/db/structure.sql b/db/structure.sql index 3dde2e76c..c7a3cdcc1 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1118,7 +1118,8 @@ CREATE TABLE public.daily_statistics ( average_block_time jsonb, nodes_distribution jsonb, nodes_count integer, - locked_capacity numeric(30,0) + locked_capacity numeric(30,0), + ckb_hodl_wave jsonb ); @@ -1936,8 +1937,7 @@ 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, - ckb_hodl_waves jsonb + transaction_fee_rates jsonb ); @@ -4131,13 +4131,6 @@ CREATE INDEX index_pool_transaction_entries_on_tx_status ON public.pool_transact CREATE UNIQUE INDEX index_portfolios_on_user_id_and_address_id ON public.portfolios USING btree (user_id, address_id); --- --- Name: index_referring_cells_on_cell_output_id; Type: INDEX; Schema: public; Owner: - --- - -CREATE UNIQUE INDEX index_referring_cells_on_cell_output_id ON public.referring_cells USING btree (cell_output_id); - - -- -- Name: index_referring_cells_on_contract_id_and_cell_output_id; Type: INDEX; Schema: public; Owner: - -- @@ -4943,6 +4936,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20231218082938'), ('20240107100346'), ('20240118103947'), -('20240119131328'); +('20240119131328'), +('20240205023511'), +('20240205024238'); diff --git a/lib/scheduler.rb b/lib/scheduler.rb index 540fb5c65..19ce8e37a 100644 --- a/lib/scheduler.rb +++ b/lib/scheduler.rb @@ -39,10 +39,6 @@ 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 diff --git a/lib/tasks/migration/reset_daily_statistic_attribute.rake b/lib/tasks/migration/reset_daily_statistic_attribute.rake new file mode 100644 index 000000000..9eee6ef2e --- /dev/null +++ b/lib/tasks/migration/reset_daily_statistic_attribute.rake @@ -0,0 +1,11 @@ +namespace :migration do + desc "Usage: RAILS_ENV=production bundle exec rake 'migration:reset_daily_statistic_attribute[ckb_hodl_wave]'" + task :reset_daily_statistic_attribute, [:attribute] => :environment do |_, args| + attribute = args[:attribute] + DailyStatistic.where(attribute => nil).order("id asc").each do |ds| + puts ds.created_at_unixtimestamp + ds.reset_one!(attribute.to_sym) + end + puts "done" + end +end diff --git a/test/controllers/api/v1/address_live_cells_controller_test.rb b/test/controllers/api/v1/address_live_cells_controller_test.rb index e17842b6e..a4446429c 100644 --- a/test/controllers/api/v1/address_live_cells_controller_test.rb +++ b/test/controllers/api/v1/address_live_cells_controller_test.rb @@ -55,6 +55,7 @@ class AddressLiveCellsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_live_cell_url(address.address_hash) assert_equal ({ "cell_type" => "omiga_inscription_info", "tx_hash" => transaction.tx_hash, + "block_number" => block.number.to_s, "cell_index" => 0, "type_hash" => info.type_hash, "data" => "0x0814434b42204669737420496e736372697074696f6e04434b4249a69f54bf339dd121febe64cb0be3a2cf366a8b13ec1a5ae4bebdccb9039c7efa0040075af0750700000000000000000000e8764817000000000000000000000002", @@ -65,7 +66,7 @@ class AddressLiveCellsControllerTest < ActionDispatch::IntegrationTest "hash_type" => "type" }, "lock_script" => { "args" => address_lock.args, "code_hash" => address_lock.code_hash, "hash_type" => "type" }, - "extra_info" => { "symbol" => "CKBI", "name" => "CKB Fist Inscription", "decimal" => "8.0", "amount" => "0" } }), + "extra_info" => { "type" => "omiga_inscription", "symbol" => "CKBI", "name" => "CKB Fist Inscription", "decimal" => "8.0", "amount" => "0" } }), json["data"].first["attributes"] end diff --git a/test/controllers/api/v1/daily_statistics_controller_test.rb b/test/controllers/api/v1/daily_statistics_controller_test.rb index a5ecb0ea7..5599612c7 100644 --- a/test/controllers/api/v1/daily_statistics_controller_test.rb +++ b/test/controllers/api/v1/daily_statistics_controller_test.rb @@ -81,7 +81,7 @@ class DailyStatisticsControllerTest < ActionDispatch::IntegrationTest assert_equal DailyStatisticSerializer.new( ::DailyStatistic.order(created_at_unixtimestamp: :asc).valid_indicators, - params: { indicator: "total_dao_deposit" } + params: { indicator: "total_dao_deposit" }, ).serialized_json, response.body end @@ -131,6 +131,21 @@ class DailyStatisticsControllerTest < ActionDispatch::IntegrationTest response.body assert_equal 100, json.dig("data").size end + + test "should return ckb_hodl_wave" do + ckb_hodl_wave = { "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 } + create(:daily_statistic, created_at_unixtimestamp: 1.day.ago.to_i, ckb_hodl_wave:) + valid_get api_v1_daily_statistic_url("ckb_hodl_wave") + assert_equal 1, json.dig("data").size + end end end end diff --git a/test/controllers/api/v1/statistics_controller_test.rb b/test/controllers/api/v1/statistics_controller_test.rb index ef58105ee..796483e0b 100644 --- a/test/controllers/api/v1/statistics_controller_test.rb +++ b/test/controllers/api/v1/statistics_controller_test.rb @@ -248,25 +248,6 @@ class StatisticsControllerTest < ActionDispatch::IntegrationTest 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 diff --git a/test/factories/statistic_infos.rb b/test/factories/statistic_infos.rb index ce89cc078..1b4762578 100644 --- a/test/factories/statistic_infos.rb +++ b/test/factories/statistic_infos.rb @@ -8,6 +8,5 @@ miner_ranking { "" } blockchain_info { "MyString" } last_n_days_transaction_fee_rates { "" } - ckb_hodl_waves { "" } end end diff --git a/test/models/ckb_transaction_test.rb b/test/models/ckb_transaction_test.rb index 0bed44893..2b268481e 100644 --- a/test/models/ckb_transaction_test.rb +++ b/test/models/ckb_transaction_test.rb @@ -8,7 +8,7 @@ class CkbTransactionTest < ActiveSupport::TestCase CkbSync::Api.any_instance.stubs(:get_block_cycles).returns( [ "0x100", "0x200", "0x300", "0x400", "0x500", "0x600", "0x700", "0x800", "0x900" - ] + ], ) end @@ -34,8 +34,8 @@ class CkbTransactionTest < ActiveSupport::TestCase compact_target: "0x1000", length: "0x07d0", number: "0x0", - start_number: "0x0" - ) + start_number: "0x0", + ), ) node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER) create(:block, :with_block_hash, number: node_block.header.number - 1) @@ -116,7 +116,7 @@ class CkbTransactionTest < ActiveSupport::TestCase prepare_node_data block = Block.last ckb_transaction = create(:ckb_transaction, :with_single_output, - is_cellbase: true, block: block) + is_cellbase: true, block:) expected_attributes = %i( id capacity occupied_capacity address_hash target_block_number base_reward commit_reward proposal_reward secondary_reward status consumed_tx_hash @@ -154,7 +154,7 @@ class CkbTransactionTest < ActiveSupport::TestCase ckb_transaction = create(:ckb_transaction, :with_multiple_inputs_and_outputs) block = create(:block, :with_block_hash) - consumed_tx = create(:ckb_transaction, block: block) + consumed_tx = create(:ckb_transaction, block:) ckb_transaction.outputs.update(consumed_by: consumed_tx, status: "dead") assert_equal ["dead"], ckb_transaction.display_outputs.pluck(:status).uniq @@ -211,13 +211,13 @@ class CkbTransactionTest < ActiveSupport::TestCase compensation_ended_timestamp: ended_block.timestamp, locked_until_block_number: ckb_transaction.block.number, locked_until_block_timestamp: ckb_transaction.block.timestamp, - interest: interest, + interest:, cell_type: nervos_dao_withdrawing_cell.cell_type, cell_index: nervos_dao_withdrawing_cell.cell_index, since: { raw: "0x0000000000000000", - median_timestamp: "0" - } + median_timestamp: "0", + }, ).sort expected_attributes = %i( id from_cellbase capacity occupied_capacity address_hash generated_tx_hash compensation_started_block_number @@ -232,13 +232,13 @@ class CkbTransactionTest < ActiveSupport::TestCase test "#display_inputs should return dao display input when previous cell type is nervos_dao_deposit" do DaoCompensationCalculator.any_instance.stubs(:call).returns(100800000000) block = create(:block, :with_block_hash, timestamp: Time.current.to_i) - ckb_transaction = create(:ckb_transaction, block: block, + ckb_transaction = create(:ckb_transaction, block:, tx_hash: "0xe8a116ec65f7d2d0d4748ba2bbcf8691cbd31202908ccfa3a975414fef801042") deposit_output_cell = create(:cell_output, block: ckb_transaction.block, capacity: 138 * 10**8, tx_hash: "0xe8a116ec65f7d2d0d4748ba2bbcf8691cbd31202908ccfa3a975414fef801042", cell_index: 0, - ckb_transaction: ckb_transaction, + ckb_transaction:, consumed_by: ckb_transaction, status: "dead", cell_type: "nervos_dao_deposit", @@ -278,13 +278,13 @@ class CkbTransactionTest < ActiveSupport::TestCase compensation_ended_block_number: ended_block.number, compensation_started_timestamp: started_block.timestamp, compensation_ended_timestamp: ended_block.timestamp, - interest: interest, + interest:, cell_type: deposit_output_cell.cell_type, cell_index: deposit_output_cell.cell_index, since: { raw: "0x0000000000000000", - median_timestamp: "0" - } + median_timestamp: "0", + }, ).sort expected_attributes = %i( id from_cellbase capacity occupied_capacity address_hash generated_tx_hash interest cell_type @@ -309,7 +309,7 @@ class CkbTransactionTest < ActiveSupport::TestCase ).sort consumed_tx_hash = dao_output.live? ? nil : dao_output.consumed_by.tx_hash expected_display_output = CkbUtils.hash_value_to_s(id: dao_output.id, - capacity: dao_output.capacity, occupied_capacity: dao_output.occupied_capacity, address_hash: dao_output.address_hash, status: dao_output.status, consumed_tx_hash: consumed_tx_hash, cell_type: dao_output.cell_type).sort + capacity: dao_output.capacity, occupied_capacity: dao_output.occupied_capacity, address_hash: dao_output.address_hash, status: dao_output.status, consumed_tx_hash:, cell_type: dao_output.cell_type).sort display_outputs = ckb_transaction.display_outputs assert_equal expected_attributes - display_outputs.first.keys, [] assert_equal expected_display_output, display_outputs.first.sort @@ -352,7 +352,7 @@ class CkbTransactionTest < ActiveSupport::TestCase cell_index: udt_cell_output.cell_index, cell_type: udt_cell_output.cell_type, since: { raw: "0x0000000000000000", median_timestamp: "0" }, - extra_info: udt_cell_output.udt_info + extra_info: udt_cell_output.udt_info, ) display_inputs = ckb_transaction.display_inputs o = display_inputs.first @@ -392,7 +392,7 @@ class CkbTransactionTest < ActiveSupport::TestCase status: udt_cell_output.status, consumed_tx_hash: nil, cell_type: udt_cell_output.cell_type, - extra_info: udt_cell_output.udt_info + extra_info: udt_cell_output.udt_info, ) o = udt_output_transaction.display_outputs.first assert_equal expected_attributes - o.keys, [] @@ -434,7 +434,7 @@ class CkbTransactionTest < ActiveSupport::TestCase cell_index: m_nft_cell_output.cell_index, cell_type: m_nft_cell_output.cell_type, since: { raw: "0x0000000000000000", median_timestamp: "0" }, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) display_inputs = ckb_transaction.display_inputs o = display_inputs.first @@ -477,7 +477,7 @@ class CkbTransactionTest < ActiveSupport::TestCase cell_index: m_nft_cell_output.cell_index, cell_type: m_nft_cell_output.cell_type, since: { raw: "0x0000000000000000", median_timestamp: "0" }, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) display_inputs = ckb_transaction.display_inputs o = display_inputs.first @@ -539,9 +539,9 @@ class CkbTransactionTest < ActiveSupport::TestCase cell_type: m_nft_cell_output.cell_type, since: { raw: "0x0000000000000000", - median_timestamp: "0" + median_timestamp: "0", }, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) display_inputs = ckb_transaction.display_inputs o = display_inputs.first @@ -571,7 +571,7 @@ class CkbTransactionTest < ActiveSupport::TestCase status: m_nft_cell_output.status, consumed_tx_hash: nil, cell_type: m_nft_cell_output.cell_type, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) o = m_nft_output_transaction.display_outputs.first assert_equal expected_attributes - o.keys, [] @@ -600,7 +600,7 @@ class CkbTransactionTest < ActiveSupport::TestCase status: m_nft_cell_output.status, consumed_tx_hash: nil, cell_type: m_nft_cell_output.cell_type, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) o = m_nft_output_transaction.display_outputs.first assert_equal expected_attributes - o.keys, [] @@ -654,7 +654,7 @@ class CkbTransactionTest < ActiveSupport::TestCase status: m_nft_cell_output.status, consumed_tx_hash: nil, cell_type: m_nft_cell_output.cell_type, - extra_info: m_nft_cell_output.m_nft_info + extra_info: m_nft_cell_output.m_nft_info, ) display_outputs = m_nft_output_transaction.display_outputs o = display_outputs.first @@ -704,16 +704,18 @@ class CkbTransactionTest < ActiveSupport::TestCase udt_type: "nrc_721_token", nrc_factory_cell_id: nrc_factory_cell.id) address = create(:address) - udt_account = create(:udt_account, udt: udt, - address: address, + udt_account = create(:udt_account, udt:, + address:, nft_token_id: "22c70f8e24a90dcccc7eb1ea669ac6cfecab095a1886af01d71612fdb3c836c8") factory_info = { symbol: "TTF", amount: "", decimal: "", type_hash: "0x", published: true, - display_name: "Test token factory", nan: "" } + display_name: "Test token factory", uan: "" + } token_info = { symbol: "TTF", amount: udt_account.nft_token_id, decimal: "6", type_hash: "0x", published: true, - display_name: "kingdom fat coin", uan: "" } + display_name: "kingdom fat coin", uan: "" + } display_outputs = nrc_721_token_output_transaction.display_outputs assert_equal factory_info.to_a, display_outputs.first[:nrc_721_token_info].to_a diff --git a/test/services/charts/daily_statistic_generator_test.rb b/test/services/charts/daily_statistic_generator_test.rb index 2e2d6cbbb..38342c773 100644 --- a/test/services/charts/daily_statistic_generator_test.rb +++ b/test/services/charts/daily_statistic_generator_test.rb @@ -59,10 +59,10 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase dao: "0xeeaf2fe1baa6df2e577fda67799223009ca127a6d1e30c00002dc77aa42b0007") create(:address, address_hash: "ckb1qyqy6mtud5sgctjwgg6gydd0ea05mr339lnslczzrc", balance: 10**8 * 1000) - tx = create(:ckb_transaction, block: block, + tx = create(:ckb_transaction, block:, block_timestamp: block.timestamp) create(:cell_output, cell_type: "nervos_dao_deposit", - ckb_transaction: tx, block: block, capacity: 10**8 * 1000, block_timestamp: (Time.current - 1.day).end_of_day.to_i * 1000, occupied_capacity: 6100000000, dao: block.dao) + ckb_transaction: tx, block:, capacity: 10**8 * 1000, block_timestamp: (Time.current - 1.day).end_of_day.to_i * 1000, occupied_capacity: 6100000000, dao: block.dao) CkbSync::Api.any_instance.stubs(:get_tip_header).returns( CKB::Types::BlockHeader.new( compact_target: "0x1a33cadd", @@ -76,8 +76,8 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase extra_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", version: "0x0", epoch: "0x70803b9000045", - dao: "0xeeaf2fe1baa6df2e577fda67799223009ca127a6d1e30c00002dc77aa42b0007" - ) + dao: "0xeeaf2fe1baa6df2e577fda67799223009ca127a6d1e30c00002dc77aa42b0007", + ), ) count = ::DailyStatistic.count Charts::DailyStatisticGenerator.new(@datetime).call @@ -234,7 +234,7 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase create(:cell_output, :with_full_transaction, block_timestamp: @datetime.to_i * 1000, block: @block), create(:cell_output, :with_full_transaction, - block_timestamp: @datetime.to_i * 1000, block: @block) + block_timestamp: @datetime.to_i * 1000, block: @block), ] CellOutput.where(id: cells.map(&:id)).update_all(consumed_block_timestamp: (@datetime.to_i + 10) * 1000) is_from_scratch = true @@ -412,24 +412,24 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase max_n = 119 ranges = [[0, 180]] + (180..(180 + max_n)).map { |n| [n, n + 1] } temp_epoch_time_distribution = - ranges.each_with_index.map { |range, index| + ranges.each_with_index.map do |range, index| milliseconds_start = range[0] * 60 * 1000 milliseconds_end = range[1] * 60 * 1000 - if index.zero? - epoch_count = ::EpochStatistic.where( - "epoch_time > 0 and epoch_time <= ?", milliseconds_end - ).count - elsif index == max_n + 1 - epoch_count = ::EpochStatistic.where("epoch_time > ?", + epoch_count = if index.zero? + ::EpochStatistic.where( + "epoch_time > 0 and epoch_time <= ?", milliseconds_end + ).count + elsif index == max_n + 1 + ::EpochStatistic.where("epoch_time > ?", milliseconds_start).count - else - epoch_count = ::EpochStatistic.where( - "epoch_time > ? and epoch_time <= ?", milliseconds_start, milliseconds_end - ).count - end + else + ::EpochStatistic.where( + "epoch_time > ? and epoch_time <= ?", milliseconds_start, milliseconds_end + ).count + end [range[1], epoch_count] - }.compact + end.compact epoch_time_distribution = Charts::DailyStatisticGenerator.new(@datetime).call.epoch_time_distribution assert_equal temp_epoch_time_distribution, epoch_time_distribution end @@ -454,13 +454,13 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase interval = 499 start_epoch_number = [0, tip_epoch_number - interval].max - temp_epoch_length_distribution = ranges.each_with_index.map { |range, _index| + temp_epoch_length_distribution = ranges.each_with_index.map do |range, _index| epoch_count = ::EpochStatistic.where("epoch_number >= ? and epoch_number <= ?", start_epoch_number, tip_epoch_number).where( "epoch_length > ? and epoch_length <= ?", range[0], range[1] ).count [range[1], epoch_count] - }.compact + end.compact epoch_length_distribution = Charts::DailyStatisticGenerator.new.call.epoch_length_distribution assert_equal temp_epoch_length_distribution, epoch_length_distribution end