diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..7296291ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Environment** + +- **Operating system**: Linux / macOS / Windows +- **Arch**: x64 +- **Installation**: GitHub Release + +**Describe the bug** + +_Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._ + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. excite come command +3. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..36014cde5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/question-issue-template.md b/.github/ISSUE_TEMPLATE/question-issue-template.md new file mode 100644 index 000000000..289201b96 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question-issue-template.md @@ -0,0 +1,10 @@ +--- +name: Question issue template +about: Ask a question +title: '' +labels: help wanted +assignees: '' + +--- + + diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c969d65..4fbec1c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ -# [v0.10.1](https://github.com/shaojunda/ckb-explorer/compare/v0.10.0...v0.10.1) (2020-07-17) +# [v0.11.0](https://github.com/nervosnetwork/ckb-explorer/compare/v0.10.1...v0.11.0) (2020-07-30) + + +### Bug Fixes +* [#703](https://github.com/nervosnetwork/ckb-explorer/pull/703): fix address nil when update udt accounts +* [#705](https://github.com/nervosnetwork/ckb-explorer/pull/705): fix zero division + +### Performance Improvements + +* [#697](https://github.com/nervosnetwork/ckb-explorer/pull/697): perf xxx transactions list + * perf address_dao_transactions + * perf address_transactions + * perf address_udt_transactions + * perf block_udt_transactions + * perf contract_transactions + * perf udt_transactions + + +# [v0.10.1](https://github.com/nervosnetwork/ckb-explorer/compare/v0.10.0...v0.10.1) (2020-07-17) ### Bug Fixes diff --git a/Gemfile.lock b/Gemfile.lock index 29da8b53c..e31a78eb9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,7 +163,7 @@ GEM i18n (1.8.3) concurrent-ruby (~> 1.0) jaro_winkler (1.5.2) - json (2.2.0) + json (2.3.1) kaminari (1.2.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.1) diff --git a/app/controllers/api/v1/address_dao_transactions_controller.rb b/app/controllers/api/v1/address_dao_transactions_controller.rb index 634dab64a..339b2a204 100644 --- a/app/controllers/api/v1/address_dao_transactions_controller.rb +++ b/app/controllers/api/v1/address_dao_transactions_controller.rb @@ -8,10 +8,14 @@ def show address = Address.find_address!(params[:id]) raise Api::V1::Exceptions::AddressNotFoundError if address.is_a?(NullAddress) - ckb_dao_transactions = address.ckb_dao_transactions.recent.page(@page).per(@page_size) - options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: @page, page_size: @page_size).call - - render json: CkbTransactionSerializer.new(ckb_dao_transactions, options.merge({ params: { previews: true } })) + ckb_dao_transactions = address.ckb_dao_transactions.select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).recent.page(@page).per(@page_size) + json = + Rails.cache.realize(ckb_dao_transactions.cache_key, version: ckb_dao_transactions.cache_version) do + options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: @page, page_size: @page_size).call + CkbTransactionsSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json + end + + render json: json end private diff --git a/app/controllers/api/v1/address_transactions_controller.rb b/app/controllers/api/v1/address_transactions_controller.rb index ac8113d3c..caef4b99f 100644 --- a/app/controllers/api/v1/address_transactions_controller.rb +++ b/app/controllers/api/v1/address_transactions_controller.rb @@ -8,10 +8,15 @@ def show @address = Address.find_address!(params[:id]) raise Api::V1::Exceptions::AddressNotFoundError if @address.is_a?(NullAddress) - @ckb_transactions = @address.custom_ckb_transactions.recent.page(@page).per(@page_size) - @options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: @ckb_transactions, page: @page, page_size: @page_size).call + @ckb_transactions = @address.custom_ckb_transactions.select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).recent.page(@page).per(@page_size) - render json: json_result + json = + Rails.cache.realize(@ckb_transactions.cache_key, version: @ckb_transactions.cache_version) do + @options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: @ckb_transactions, page: @page, page_size: @page_size).call + json_result + end + + render json: json end private @@ -33,16 +38,16 @@ def pagination_params end def json_result - ckb_transaction_serializer = CkbTransactionSerializer.new(@ckb_transactions, @options.merge({ params: { previews: true, address: @address } })) + ckb_transaction_serializer = CkbTransactionsSerializer.new(@ckb_transactions, @options.merge(params: { previews: true, address: @address })) if QueryKeyUtils.valid_address?(params[:id]) if @address.address_hash == @address.query_address - ckb_transaction_serializer + ckb_transaction_serializer.serialized_json else ckb_transaction_serializer.serialized_json.gsub(@address.address_hash, @address.query_address) end else - ckb_transaction_serializer + ckb_transaction_serializer.serialized_json end end end diff --git a/app/controllers/api/v1/address_udt_transactions_controller.rb b/app/controllers/api/v1/address_udt_transactions_controller.rb index 28830feef..a3e1c1d1e 100644 --- a/app/controllers/api/v1/address_udt_transactions_controller.rb +++ b/app/controllers/api/v1/address_udt_transactions_controller.rb @@ -12,10 +12,14 @@ def show udt = Udt.find_by(type_hash: params[:type_hash], published: true) raise Api::V1::Exceptions::UdtNotFoundError if udt.blank? - ckb_dao_transactions = address.ckb_udt_transactions(params[:type_hash]).recent.page(@page).per(@page_size) - options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: @page, page_size: @page_size).call - - render json: CkbTransactionSerializer.new(ckb_dao_transactions, options.merge({ params: { previews: true } })) + ckb_dao_transactions = address.ckb_udt_transactions(params[:type_hash]).select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).recent.page(@page).per(@page_size) + json = + Rails.cache.realize(ckb_dao_transactions.cache_key, version: ckb_dao_transactions.cache_version) do + options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: @page, page_size: @page_size).call + CkbTransactionsSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json + end + + render json: json end private diff --git a/app/controllers/api/v1/block_transactions_controller.rb b/app/controllers/api/v1/block_transactions_controller.rb index 01834e610..45db75803 100644 --- a/app/controllers/api/v1/block_transactions_controller.rb +++ b/app/controllers/api/v1/block_transactions_controller.rb @@ -6,10 +6,14 @@ class BlockTransactionsController < ApplicationController def show block = Block.find_by!(block_hash: params[:id]) - ckb_transactions = block.ckb_transactions.order(:id).page(@page).per(@page_size) - options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + ckb_transactions = block.ckb_transactions.select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).order(:id).page(@page).per(@page_size) + json = + Rails.cache.realize(ckb_transactions.cache_key, version: ckb_transactions.cache_version) do + options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + CkbTransactionsSerializer.new(ckb_transactions, options.merge(params: { previews: true })).serialized_json + end - render json: CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true } })) + render json: json rescue ActiveRecord::RecordNotFound raise Api::V1::Exceptions::BlockTransactionsNotFoundError end diff --git a/app/controllers/api/v1/contract_transactions_controller.rb b/app/controllers/api/v1/contract_transactions_controller.rb index 8dd74a70c..318e7e029 100644 --- a/app/controllers/api/v1/contract_transactions_controller.rb +++ b/app/controllers/api/v1/contract_transactions_controller.rb @@ -7,10 +7,14 @@ def show raise Api::V1::Exceptions::ContractNotFoundError if params[:id] != DaoContract::CONTRACT_NAME dao_contract = DaoContract.default_contract - ckb_transactions = dao_contract.ckb_transactions.distinct.recent.page(@page).per(@page_size) - options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + ckb_transactions = dao_contract.ckb_transactions.select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).recent.page(@page).per(@page_size) + json = + Rails.cache.realize(ckb_transactions.cache_key, version: ckb_transactions.cache_version) do + options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + CkbTransactionsSerializer.new(ckb_transactions, options.merge(params: { previews: true })).serialized_json + end - render json: CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true } })) + render json: json end private diff --git a/app/controllers/api/v1/udt_transactions_controller.rb b/app/controllers/api/v1/udt_transactions_controller.rb index f3f6fab1d..ee6fb91db 100644 --- a/app/controllers/api/v1/udt_transactions_controller.rb +++ b/app/controllers/api/v1/udt_transactions_controller.rb @@ -1,32 +1,40 @@ -class Api::V1::UdtTransactionsController < ApplicationController - before_action :validate_query_params - before_action :validate_pagination_params, :pagination_params +module Api + module V1 + class UdtTransactionsController < ApplicationController + before_action :validate_query_params + before_action :validate_pagination_params, :pagination_params - def show - udt = Udt.find_by!(type_hash: params[:id], published: true) - ckb_transactions = udt.ckb_transactions.recent.page(@page).per(@page_size) - options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + def show + udt = Udt.find_by!(type_hash: params[:id], published: true) + ckb_transactions = udt.ckb_transactions.select(:id, :tx_hash, :block_id, :block_number, :block_timestamp, :is_cellbase).recent.page(@page).per(@page_size) + json = + Rails.cache.realize(ckb_transactions.cache_key, version: ckb_transactions.cache_version) do + options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: @page, page_size: @page_size).call + CkbTransactionsSerializer.new(ckb_transactions, options.merge(params: { previews: true })).serialized_json + end - render json: CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true } })) - rescue ActiveRecord::RecordNotFound - raise Api::V1::Exceptions::UdtNotFoundError - end + render json: json + rescue ActiveRecord::RecordNotFound + raise Api::V1::Exceptions::UdtNotFoundError + end - private + private - def validate_query_params - validator = Validations::Udt.new(params) + def validate_query_params + validator = Validations::Udt.new(params) - if validator.invalid? - errors = validator.error_object[:errors] - status = validator.error_object[:status] + if validator.invalid? + errors = validator.error_object[:errors] + status = validator.error_object[:status] - render json: errors, status: status - end - end + render json: errors, status: status + end + end - def pagination_params - @page = params[:page] || 1 - @page_size = params[:page_size] || CkbTransaction.default_per_page + def pagination_params + @page = params[:page] || 1 + @page_size = params[:page_size] || CkbTransaction.default_per_page + end + end end end diff --git a/app/models/address.rb b/app/models/address.rb index b9396bdad..309f65ce1 100644 --- a/app/models/address.rb +++ b/app/models/address.rb @@ -18,46 +18,18 @@ class Address < ApplicationRecord attr_accessor :query_address def custom_ckb_transactions - ckb_transaction_ids = account_books.select(:ckb_transaction_id).distinct - CkbTransaction.where(id: ckb_transaction_ids) + CkbTransaction.where("contained_address_ids @> array[?]::bigint[]", [id]) end def ckb_dao_transactions - ckb_transaction_ids = cell_outputs.where(cell_type: %w(nervos_dao_deposit nervos_dao_withdrawing)).select("ckb_transaction_id").distinct - CkbTransaction.where(id: ckb_transaction_ids) + CkbTransaction.where("contained_address_ids @> array[?]::bigint[]", [id]).where("tags @> array[?]::varchar[]", ["dao"]) end def ckb_udt_transactions(type_hash) - sql = - <<-SQL - SELECT - generated_by_id ckb_transaction_id - FROM - cell_outputs - WHERE - address_id = #{id} - AND - cell_type = #{CellOutput::cell_types['udt']} - AND - type_hash = '#{type_hash}' - - UNION - - SELECT - consumed_by_id ckb_transaction_id - FROM - cell_outputs - WHERE - address_id = #{id} - AND - cell_type = #{CellOutput::cell_types['udt']} - AND - type_hash = '#{type_hash}' - AND - consumed_by_id is not null - SQL - ckb_transaction_ids = CellOutput.select("ckb_transaction_id").from("(#{sql}) as cell_outputs") - CkbTransaction.where(id: ckb_transaction_ids.distinct) + udt = Udt.where(type_hash: type_hash).select(:id).first + return [] if udt.blank? + + CkbTransaction.where("contained_address_ids @> array[?]::bigint[]", [id]).where("contained_udt_ids @> array[?]::bigint[]", [udt.id]) end def lock_info diff --git a/app/models/ckb_sync/node_data_processor.rb b/app/models/ckb_sync/node_data_processor.rb index bf8c0b4d5..a5c04793b 100644 --- a/app/models/ckb_sync/node_data_processor.rb +++ b/app/models/ckb_sync/node_data_processor.rb @@ -83,7 +83,7 @@ def update_udt_accounts(udt_infos, block_timestamp) udt = Udt.find_or_create_by!(type_hash: type_hash, code_hash: ENV["SUDT_CELL_TYPE_HASH"], udt_type: "sudt") udt.update(block_timestamp: block_timestamp) if udt.block_timestamp.blank? if udt.issuer_address.blank? - issuer_address = Address.where(lock_hash: udt_type_script.args).select(:address_hash).first.address_hash + issuer_address = Address.where(lock_hash: udt_type_script.args).pick(:address_hash) udt.issuer_address = issuer_address end udt.update(args: udt_type_script.args, hash_type: udt_type_script.hash_type, issuer_address: udt.issuer_address) @@ -291,7 +291,7 @@ def revert_deposit_to_dao(dao_contract, dao_events) def update_block_contained_address_info(local_block) ApplicationRecord.transaction do - local_block.address_ids = AccountBook.where(ckb_transaction: local_block.ckb_transactions).pluck(:address_id).uniq + local_block.address_ids = local_block.ckb_transactions.pluck(:contained_address_ids).flatten.uniq local_block.save! local_block.contained_addresses.each(&method(:update_address_balance_and_ckb_transactions_count)) end @@ -392,11 +392,16 @@ def build_uncle_block(uncle_block, local_block) def build_ckb_transactions(local_block, transactions, outputs, new_dao_depositor_events, udt_infos) transactions.each_with_index.map do |transaction, transaction_index| addresses = Set.new + address_ids = Set.new + tags = Set.new + udt_ids = Set.new ckb_transaction = build_ckb_transaction(local_block, transaction, transaction_index) build_cell_inputs(transaction.inputs, ckb_transaction) - build_cell_outputs(transaction.outputs, ckb_transaction, addresses, transaction.outputs_data, outputs, new_dao_depositor_events, udt_infos) - addresses_arr = addresses.to_a - ckb_transaction.addresses << addresses_arr + build_cell_outputs(transaction.outputs, ckb_transaction, addresses, transaction.outputs_data, outputs, new_dao_depositor_events, udt_infos, address_ids, tags, udt_ids) + ckb_transaction.addresses << addresses.to_a + ckb_transaction.contained_address_ids += address_ids.to_a + ckb_transaction.tags += tags.to_a + ckb_transaction.contained_udt_ids += udt_ids.to_a ckb_transaction end @@ -440,13 +445,21 @@ def from_cell_base?(node_input) node_input.previous_output.tx_hash == CellOutput::SYSTEM_TX_HASH end - def build_cell_outputs(node_outputs, ckb_transaction, addresses, outputs_data, outputs, new_dao_depositor_events, udt_infos) + def build_cell_outputs(node_outputs, ckb_transaction, addresses, outputs_data, outputs, new_dao_depositor_events, udt_infos, address_ids, tags, udt_ids) node_outputs.each_with_index.map do |output, cell_index| address = Address.find_or_create_address(output.lock, ckb_transaction.block_timestamp) addresses << address + address_ids << address.id cell_output = build_cell_output(ckb_transaction, output, address, cell_index, outputs_data[cell_index]) outputs << cell_output - udt_infos << { type_script: output.type, address: address } if cell_output.udt? + if cell_output.udt? + udt_infos << { type_script: output.type, address: address } + tags << "udt" + udt_ids << Udt.find_or_create_by!(type_hash: output.type.compute_hash, code_hash: ENV["SUDT_CELL_TYPE_HASH"], udt_type: "sudt").id + end + if cell_output.nervos_dao_deposit? || cell_output.nervos_dao_withdrawing? + tags << "dao" + end build_deposit_dao_events(address, cell_output, ckb_transaction, new_dao_depositor_events) build_lock_script(cell_output, output.lock, address) @@ -547,6 +560,7 @@ def update_tx_fee_related_data(local_block, input_capacities, udt_infos) local_block.cell_inputs.where(from_cell_base: false, previous_cell_output_id: nil).find_in_batches(batch_size: 3500) do |cell_inputs| updated_inputs = [] updated_outputs = [] + updated_ckb_transactions = [] account_books = [] ApplicationRecord.transaction do cell_inputs.each do |cell_input| @@ -562,6 +576,7 @@ def update_tx_fee_related_data(local_block, input_capacities, udt_infos) link_previous_cell_output_to_cell_input(cell_input, previous_cell_output) update_previous_cell_output_status(ckb_transaction_id, previous_cell_output, consumed_tx.block_timestamp) account_book = link_payer_address_to_ckb_transaction(ckb_transaction_id, address_id) + update_ckb_transaction(consumed_tx, address_id, previous_cell_output, updated_ckb_transactions) build_withdraw_dao_events(address_id, ckb_transaction_id, local_block, previous_cell_output) updated_inputs << cell_input @@ -572,6 +587,7 @@ def update_tx_fee_related_data(local_block, input_capacities, udt_infos) CellInput.import!(updated_inputs, validate: false, on_duplicate_key_update: [:previous_cell_output_id]) CellOutput.import!(updated_outputs, validate: false, on_duplicate_key_update: [:consumed_by_id, :status, :consumed_block_timestamp]) AccountBook.import!(account_books, validate: false) + CkbTransaction.upsert_all(updated_ckb_transactions.uniq { |tx| tx[:id] }) end input_cache_keys = updated_inputs.map(&:cache_keys) output_cache_keys = updated_outputs.map(&:cache_keys) @@ -595,6 +611,25 @@ def link_payer_address_to_ckb_transaction(ckb_transaction_id, address_id) { ckb_transaction_id: ckb_transaction_id, address_id: address_id } end + def update_ckb_transaction(consumed_tx, address_id, previous_cell_output, updated_ckb_transactions) + tx = updated_ckb_transactions.select { |tx| tx[:id] == consumed_tx.id }.first + consumed_tx.contained_address_ids << address_id + if previous_cell_output.udt? + consumed_tx.tags << "udt" + consumed_tx.contained_udt_ids << Udt.find_or_create_by!(type_hash: previous_cell_output.node_output.type.compute_hash, code_hash: ENV["SUDT_CELL_TYPE_HASH"], udt_type: "sudt").id + end + if previous_cell_output.nervos_dao_withdrawing? + consumed_tx.tags << "dao" + end + if tx.present? + tx[:contained_address_ids] = (tx[:contained_address_ids] << consumed_tx.contained_address_ids).flatten.uniq + tx[:tags] = (tx[:tags] << consumed_tx.tags).flatten.uniq + tx[:contained_udt_ids] = (tx[:contained_udt_ids] << consumed_tx.contained_udt_ids).flatten.uniq + else + updated_ckb_transactions << { id: consumed_tx.id, contained_udt_ids: consumed_tx.contained_udt_ids.uniq, contained_address_ids: consumed_tx.contained_address_ids.uniq, tags: consumed_tx.tags.uniq, created_at: consumed_tx.created_at, updated_at: Time.current } + end + end + def update_previous_cell_output_status(ckb_transaction_id, previous_cell_output, consumed_block_timestamp) previous_cell_output.consumed_by_id = ckb_transaction_id previous_cell_output.consumed_block_timestamp = consumed_block_timestamp diff --git a/app/models/ckb_transaction.rb b/app/models/ckb_transaction.rb index 7b2a3526d..5da2fa368 100644 --- a/app/models/ckb_transaction.rb +++ b/app/models/ckb_transaction.rb @@ -15,7 +15,7 @@ class CkbTransaction < ApplicationRecord attribute :tx_hash, :ckb_hash attribute :header_deps, :ckb_array_hash, hash_length: ENV["DEFAULT_HASH_LENGTH"] - scope :recent, -> { order("block_timestamp desc nulls last") } + scope :recent, -> { order("block_timestamp desc nulls last, id desc") } scope :cellbase, -> { where(is_cellbase: true) } scope :normal, -> { where(is_cellbase: false) } scope :created_after, ->(block_timestamp) { where("block_timestamp >= ?", block_timestamp) } @@ -30,6 +30,10 @@ def self.cached_find(query_key) end end + def address_ids + attributes["address_ids"] + end + def flush_cache Rails.cache.delete([self.class.name, tx_hash]) end @@ -131,27 +135,33 @@ 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) -# witnesses :jsonb -# header_deps :binary -# cell_deps :jsonb -# live_cell_changes :integer -# capacity_involved :decimal(30, ) +# 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, ) +# contained_address_ids :bigint default([]), is an Array +# tags :string default([]), is an Array +# contained_udt_ids :bigint default([]), is an Array # # Indexes # # index_ckb_transactions_on_block_id_and_block_timestamp (block_id,block_timestamp) -# index_ckb_transactions_on_block_timestamp (block_timestamp) +# index_ckb_transactions_on_block_timestamp_and_id (block_timestamp DESC NULLS LAST,id DESC) +# index_ckb_transactions_on_contained_address_ids (contained_address_ids) USING gin +# index_ckb_transactions_on_contained_udt_ids (contained_udt_ids) USING gin # index_ckb_transactions_on_is_cellbase (is_cellbase) +# index_ckb_transactions_on_tags (tags) USING gin # index_ckb_transactions_on_tx_hash_and_block_id (tx_hash,block_id) UNIQUE # diff --git a/app/models/dao_contract.rb b/app/models/dao_contract.rb index 6e35e95dc..014416dce 100644 --- a/app/models/dao_contract.rb +++ b/app/models/dao_contract.rb @@ -14,8 +14,7 @@ def self.default_contract end 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) + CkbTransaction.where("tags @> array[?]::varchar[]", ["dao"]) end def estimated_apc(deposit_epoch = tip_block_fraction_epoch, deposited_epochs = EPOCHS_IN_ONE_NATURAL_YEAR) diff --git a/app/models/statistic_info.rb b/app/models/statistic_info.rb index 569122e8e..66e5deb55 100644 --- a/app/models/statistic_info.rb +++ b/app/models/statistic_info.rb @@ -102,6 +102,6 @@ def blocks_count(interval = average_block_time_interval) end def tip_block - @tip_block ||= Block.recent.first + @tip_block ||= Block.recent.first || OpenStruct.new(number: 0, epoch: 0, length: 0, start_number: 0, difficulty: 0) end end diff --git a/app/models/udt.rb b/app/models/udt.rb index f5d31b5ca..65c055b58 100644 --- a/app/models/udt.rb +++ b/app/models/udt.rb @@ -10,32 +10,7 @@ class Udt < ApplicationRecord attribute :code_hash, :ckb_hash def ckb_transactions - sql = - <<-SQL - SELECT - generated_by_id ckb_transaction_id - FROM - cell_outputs - WHERE - cell_type = #{CellOutput::cell_types['udt']} - AND - type_hash = '#{type_hash}' - - UNION - - SELECT - consumed_by_id ckb_transaction_id - FROM - cell_outputs - WHERE - cell_type = #{CellOutput::cell_types['udt']} - AND - type_hash = '#{type_hash}' - AND - consumed_by_id is not null - SQL - ckb_transaction_ids = CellOutput.select("ckb_transaction_id").from("(#{sql}) as cell_outputs") - CkbTransaction.where(id: ckb_transaction_ids.distinct) + CkbTransaction.where("contained_udt_ids @> array[?]::bigint[]", [id]) end def h24_ckb_transactions_count diff --git a/app/serializers/ckb_transactions_serializer.rb b/app/serializers/ckb_transactions_serializer.rb new file mode 100644 index 000000000..03ab92f1f --- /dev/null +++ b/app/serializers/ckb_transactions_serializer.rb @@ -0,0 +1,27 @@ +class CkbTransactionsSerializer + include FastJsonapi::ObjectSerializer + + attributes :is_cellbase + + attribute :transaction_hash, &:tx_hash + + attribute :block_number do |object| + object.block_number.to_s + end + + attribute :block_timestamp do |object| + object.block_timestamp.to_s + end + + attribute :display_inputs do |object, params| + params && params[:previews] ? object.display_inputs(previews: true) : object.display_inputs + end + + attribute :display_outputs do |object, params| + params && params[:previews] ? object.display_outputs(previews: true) : object.display_outputs + end + + attribute :income do |object, params| + params && params[:previews] && params[:address] ? object.income(params[:address]) : nil + end +end diff --git a/app/services/charts/daily_statistic_generator.rb b/app/services/charts/daily_statistic_generator.rb index c8f6c451f..eae38f7eb 100644 --- a/app/services/charts/daily_statistic_generator.rb +++ b/app/services/charts/daily_statistic_generator.rb @@ -56,7 +56,7 @@ def avg_block_time_rolling_by_hour_sql avg(avg_block_time_per_hour) over(order by stat_timestamp rows between 7 * 24 preceding and current row) as avg_bt1 from block_time_statistics ) - -- 804 = 24 * 35, show data for the last 35 days + -- 840 = 24 * 35, show data for the last 35 days select stat_timestamp, round(avg_bt, 2) avg_bt1, round(avg_bt1, 2) avg_bt2 from avg_block_time_24_hours_rolling_by_hour where stat_timestamp >= #{35.days.ago.end_of_day.to_i} order by stat_timestamp limit 840 SQL end @@ -75,6 +75,7 @@ def epoch_length_distribution }.compact end + # [0, 180] 0-180 minutes def epoch_time_distribution max_n = 119 ranges = [[0, 180]] + (180..(180 + max_n)).map { |n| [n, n + 1] } diff --git a/app/workers/address_average_deposit_time_generator.rb b/app/workers/address_average_deposit_time_generator.rb index 3b6349414..9ce0e6732 100644 --- a/app/workers/address_average_deposit_time_generator.rb +++ b/app/workers/address_average_deposit_time_generator.rb @@ -33,6 +33,9 @@ def cal_average_deposit_time(address) memo + nervos_dao_deposit_cell.capacity * (ended_at - nervos_dao_deposit_cell.block_timestamp) / milliseconds_in_day end - ((sum_interest_bearing + sum_uninterest_bearing) / (interest_bearing_deposits + uninterest_bearing_deposits)).truncate(6) + total_deposits = interest_bearing_deposits + uninterest_bearing_deposits + return 0 if total_deposits.zero? + + ((sum_interest_bearing + sum_uninterest_bearing) / total_deposits).truncate(6) end end diff --git a/db/migrate/20200716174806_add_tags_address_ids_and_udt_type_hashes_to_ckb_transaction.rb b/db/migrate/20200716174806_add_tags_address_ids_and_udt_type_hashes_to_ckb_transaction.rb new file mode 100644 index 000000000..4d71912ec --- /dev/null +++ b/db/migrate/20200716174806_add_tags_address_ids_and_udt_type_hashes_to_ckb_transaction.rb @@ -0,0 +1,13 @@ +class AddTagsAddressIdsAndUdtTypeHashesToCkbTransaction < ActiveRecord::Migration[6.0] + disable_ddl_transaction! + + def change + add_column :ckb_transactions, :contained_address_ids, :bigint, array: true, default: [] + add_column :ckb_transactions, :tags, :string, array: true, default: [] + add_column :ckb_transactions, :contained_udt_ids, :bigint, array: true, default: [] + + add_index :ckb_transactions, :contained_address_ids, using: :gin, algorithm: :concurrently + add_index :ckb_transactions, :tags, using: :gin, algorithm: :concurrently + add_index :ckb_transactions, :contained_udt_ids, using: :gin, algorithm: :concurrently + end +end diff --git a/db/migrate/20200729174146_adjust_block_timestamp_index.rb b/db/migrate/20200729174146_adjust_block_timestamp_index.rb new file mode 100644 index 000000000..1847f3f06 --- /dev/null +++ b/db/migrate/20200729174146_adjust_block_timestamp_index.rb @@ -0,0 +1,7 @@ +class AdjustBlockTimestampIndex < ActiveRecord::Migration[6.0] + disable_ddl_transaction! + def change + remove_index :ckb_transactions, name: :index_ckb_transactions_on_block_timestamp, column: :block_timestamp, algorithm: :concurrently if index_exists?(:ckb_transactions, :block_timestamp) + add_index :ckb_transactions, [:block_timestamp, :id], order: { block_timestamp: "DESC NULLS LAST", id: "desc" }, algorithm: :concurrently + end +end diff --git a/db/schema.rb b/db/schema.rb index 22b304499..43c1abdc3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_07_14_044614) do +ActiveRecord::Schema.define(version: 2020_07_29_174146) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -182,9 +182,15 @@ t.jsonb "cell_deps" t.integer "live_cell_changes" t.decimal "capacity_involved", precision: 30 + t.bigint "contained_address_ids", default: [], array: true + t.string "tags", default: [], array: true + t.bigint "contained_udt_ids", default: [], array: true t.index ["block_id", "block_timestamp"], name: "index_ckb_transactions_on_block_id_and_block_timestamp" - t.index ["block_timestamp"], name: "index_ckb_transactions_on_block_timestamp", order: "DESC NULLS LAST" + t.index ["block_timestamp", "id"], name: "index_ckb_transactions_on_block_timestamp_and_id", order: { block_timestamp: "DESC NULLS LAST", id: :desc } + t.index ["contained_address_ids"], name: "index_ckb_transactions_on_contained_address_ids", using: :gin + t.index ["contained_udt_ids"], name: "index_ckb_transactions_on_contained_udt_ids", using: :gin t.index ["is_cellbase"], name: "index_ckb_transactions_on_is_cellbase" + t.index ["tags"], name: "index_ckb_transactions_on_tags", using: :gin t.index ["tx_hash", "block_id"], name: "index_ckb_transactions_on_tx_hash_and_block_id", unique: true end diff --git a/lib/tasks/migration/fill_address_ids_tags_and_udt_ids_to_ckb_transaction.rake b/lib/tasks/migration/fill_address_ids_tags_and_udt_ids_to_ckb_transaction.rake new file mode 100644 index 000000000..24c78d2e4 --- /dev/null +++ b/lib/tasks/migration/fill_address_ids_tags_and_udt_ids_to_ckb_transaction.rake @@ -0,0 +1,36 @@ +namespace :migration do + desc "Usage: RAILS_ENV=production bundle exec rake migration:fill_address_ids_tags_and_udt_ids_to_ckb_transaction" + task fill_address_ids_tags_and_udt_ids_to_ckb_transaction: :environment do + progress_bar = ProgressBar.create(total: CkbTransaction.count, format: "%e %B %p%% %c/%C") + CkbTransaction.order(:id).find_in_batches do |txs| + values = txs.map do |tx| + if tx.outputs.udt.present? + tx.tags << "udt" + type_hashes = tx.outputs.udt.pluck(:type_hash).uniq + tx.contained_udt_ids += Udt.where(type_hash: type_hashes).pluck(:id) + end + + if tx.outputs.where(cell_type: %w(nervos_dao_deposit nervos_dao_withdrawing)).present? + tx.tags << "dao" + end + + if tx.inputs.udt.present? + tx.tags << "udt" + type_hashes = tx.outputs.udt.pluck(:type_hash).uniq + tx.contained_udt_ids += Udt.where(type_hash: type_hashes).pluck(:id) + end + + if tx.inputs.nervos_dao_withdrawing.present? + tx.tags << "dao" + end + progress_bar.increment + + { id: tx.id, contained_address_ids: tx.addresses.pluck(:id).uniq, tags: tx.tags.uniq, contained_udt_ids: tx.contained_udt_ids.uniq, created_at: tx.created_at, updated_at: Time.current } + end + + CkbTransaction.upsert_all(values) if values.present? + end + + puts "done" + end +end diff --git a/test/controllers/api/v1/address_dao_transactions_controller_test.rb b/test/controllers/api/v1/address_dao_transactions_controller_test.rb index 740ce2323..ab2afc4e6 100644 --- a/test/controllers/api/v1/address_dao_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_dao_transactions_controller_test.rb @@ -64,7 +64,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json, response.body end test "should return corresponding ckb transactions with given lock hash" do @@ -78,7 +78,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_dao_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_dao_transactions, options.merge(params: { previews: true })).serialized_json, response.body end test "should contain right keys in the serialized object when call show" do @@ -89,7 +89,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by id" do @@ -152,7 +152,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_dao_transaction_url(address.address_hash), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_dao_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -168,7 +168,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_dao_transaction_url(address.address_hash), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_dao_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -183,7 +183,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_dao_transaction_url(address.address_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_dao_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body end @@ -198,7 +198,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_dao_transaction_url(address.address_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_dao_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_dao_transactions, options.merge(params: { previews: true })).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body @@ -242,18 +242,6 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_dao_transaction_url(address.address_hash) assert_equal links.stringify_keys.sort, json["links"].sort end - - private - - def fake_dao_deposit_transaction(dao_cell_count, address) - block = create(:block, :with_block_hash) - dao_cell_count.times do |number| - ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block, address: address) - ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block, address: address) - generated_by = number % 2 == 0 ? ckb_transaction2 : ckb_transaction1 - create(:cell_output, ckb_transaction: generated_by, cell_index: number, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: generated_by, block: block, capacity: 10**8 * 1000, cell_type: "nervos_dao_deposit", address: address) - end - end end end end diff --git a/test/controllers/api/v1/address_transactions_controller_test.rb b/test/controllers/api/v1/address_transactions_controller_test.rb index fed1c0123..476331d20 100644 --- a/test/controllers/api/v1/address_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_transactions_controller_test.rb @@ -74,7 +74,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json, response.body end test "should return corresponding ckb transactions with given lock hash" do @@ -87,7 +87,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json, response.body end test "should contain right keys in the serialized object when call show" do @@ -97,18 +97,18 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "should return correct income" do address = create(:address) block = create(:block, :with_block_hash) - generated_ckb_transaction = create(:ckb_transaction, block: block, block_timestamp: "1567131126594") + generated_ckb_transaction = create(:ckb_transaction, block: block, block_timestamp: "1567131126594", contained_address_ids: [address.id]) create(:cell_output, capacity: 10**8 * 8, ckb_transaction: generated_ckb_transaction, block: generated_ckb_transaction.block, tx_hash: generated_ckb_transaction.tx_hash, cell_index: 0, generated_by: generated_ckb_transaction, address: address) - consumed_ckb_transaction = create(:ckb_transaction, block: block, block_timestamp: "1567131126595") + consumed_ckb_transaction = create(:ckb_transaction, block: block, block_timestamp: "1567131126595", contained_address_ids: [address.id]) - generated_ckb_transaction1 = create(:ckb_transaction, block: block, block_timestamp: "1567131126596") + generated_ckb_transaction1 = create(:ckb_transaction, block: block, block_timestamp: "1567131126596", contained_address_ids: [address.id]) create(:cell_output, capacity: 10**8 * 8, ckb_transaction: generated_ckb_transaction1, block: generated_ckb_transaction1.block, tx_hash: generated_ckb_transaction1.tx_hash, cell_index: 0, generated_by: generated_ckb_transaction1, address: address) create(:cell_output, capacity: 10**8 * 6, ckb_transaction: consumed_ckb_transaction, block: consumed_ckb_transaction.block, tx_hash: consumed_ckb_transaction.tx_hash, cell_index: 0, generated_by: generated_ckb_transaction, consumed_by: consumed_ckb_transaction, address: address) address.ckb_transactions << [generated_ckb_transaction1, consumed_ckb_transaction, generated_ckb_transaction] @@ -179,7 +179,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_transaction_url(address.address_hash), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -194,7 +194,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_transaction_url(address.address_hash), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -208,7 +208,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_transaction_url(address.address_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json assert_equal response_transaction, response.body end @@ -222,7 +222,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_transaction_url(address.address_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_ckb_transactions, options.merge({ params: { previews: true, address: address } })).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body @@ -268,7 +268,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest test "should return up to ten display_inputs" do address = create(:address) block = create(:block, :with_block_hash) - ckb_transaction = create(:ckb_transaction, :with_multiple_inputs_and_outputs, block: block) + ckb_transaction = create(:ckb_transaction, :with_multiple_inputs_and_outputs, block: block, contained_address_ids: [address.id]) address.ckb_transactions << ckb_transaction valid_get api_v1_address_transaction_url(address.address_hash) @@ -280,7 +280,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest test "should return up to ten display_outputs" do address = create(:address) block = create(:block, :with_block_hash) - ckb_transaction = create(:ckb_transaction, :with_multiple_inputs_and_outputs, block: block) + ckb_transaction = create(:ckb_transaction, :with_multiple_inputs_and_outputs, block: block, contained_address_ids: [address.id]) address.ckb_transactions << ckb_transaction valid_get api_v1_address_transaction_url(address.address_hash) diff --git a/test/controllers/api/v1/address_udt_transactions_controller_test.rb b/test/controllers/api/v1/address_udt_transactions_controller_test.rb index 1368afdba..8d74005ee 100644 --- a/test/controllers/api/v1/address_udt_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_udt_transactions_controller_test.rb @@ -83,7 +83,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_udt_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_udt_transactions, options.merge(params: { previews: true })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_udt_transactions, options.merge(params: { previews: true })).serialized_json, response.body end test "should contain right keys in the serialized object when call show" do @@ -94,7 +94,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by id" do @@ -173,7 +173,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_udt_transaction_url(address.address_hash, type_hash: udt.type_hash), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_udt_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -189,7 +189,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_udt_transaction_url(address.address_hash, type_hash: udt.type_hash), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_udt_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -204,7 +204,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_udt_transaction_url(address.address_hash, type_hash: udt.type_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_udt_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body end @@ -220,7 +220,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_address_udt_transaction_url(address.address_hash, type_hash: udt.type_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: address_udt_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(address_udt_transactions, options.merge(params: { previews: true })).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body diff --git a/test/controllers/api/v1/block_transactions_controller_test.rb b/test/controllers/api/v1/block_transactions_controller_test.rb index 6e99989e2..18530e66d 100644 --- a/test/controllers/api/v1/block_transactions_controller_test.rb +++ b/test/controllers/api/v1/block_transactions_controller_test.rb @@ -82,7 +82,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest ckb_transactions = block.ckb_transactions.order(:id).page(page).per(page_size) options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_transactions, options).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_transactions, options).serialized_json, response.body end test "should contain right keys in the serialized object when call show" do @@ -92,7 +92,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "returned income should be null" do @@ -161,7 +161,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_block_transaction_url(block.block_hash), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: block_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(block_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(block_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -176,7 +176,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_block_transaction_url(block.block_hash), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: block_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(block_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(block_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -191,7 +191,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_block_transaction_url(block.block_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: block_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(block_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(block_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body end @@ -205,7 +205,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_block_transaction_url(block.block_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: block_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(block_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(block_ckb_transactions, options).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body diff --git a/test/controllers/api/v1/blocks_controller_test.rb b/test/controllers/api/v1/blocks_controller_test.rb index 99c974bd4..cf9047a31 100644 --- a/test/controllers/api/v1/blocks_controller_test.rb +++ b/test/controllers/api/v1/blocks_controller_test.rb @@ -46,7 +46,10 @@ class BlocksControllerTest < ActionDispatch::IntegrationTest end test "should get serialized objects" do - create_list(:block, 15, :with_block_hash) + 15.times do |number| + create(:block, :with_block_hash, timestamp: 2.days.ago.to_i + number) + + end block_timestamps = Block.recent.limit(ENV["HOMEPAGE_BLOCK_RECORDS_COUNT"].to_i).pluck(:timestamp) blocks = Block.where(timestamp: block_timestamps).select(:id, :miner_hash, :number, :timestamp, :reward, :ckb_transactions_count, :live_cell_changes).recent diff --git a/test/controllers/api/v1/contract_transactions_controller_test.rb b/test/controllers/api/v1/contract_transactions_controller_test.rb index 90ac61859..8d10c1f30 100644 --- a/test/controllers/api/v1/contract_transactions_controller_test.rb +++ b/test/controllers/api/v1/contract_transactions_controller_test.rb @@ -49,16 +49,17 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_transactions, options.merge({ params: { previews: true } })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_transactions, options.merge({ params: { previews: true } })).serialized_json, response.body end test "should contain right keys in the serialized transaction when call show" do - fake_dao_deposit_transaction(5) + address = create(:address) + fake_dao_deposit_transaction(5, address) valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME) response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by give contract name" do @@ -100,14 +101,16 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest end test "should return 10 records when page and page_size are not set" do - fake_dao_deposit_transaction(30) + address = create(:address) + fake_dao_deposit_transaction(30, address) valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME) assert_equal 10, json["data"].size end test "should return corresponding page's records when page is set and page_size is not set" do - fake_dao_deposit_transaction(30) + address = create(:address) + fake_dao_deposit_transaction(30, address) page = 2 page_size = 10 dao_contract = DaoContract.default_contract @@ -116,14 +119,15 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: contract_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(contract_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(contract_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size end test "should return corresponding records when page is not set and page_size is set" do - fake_dao_deposit_transaction(15) + address = create(:address) + fake_dao_deposit_transaction(15, address) page = 1 page_size = 12 dao_contract = DaoContract.default_contract @@ -132,14 +136,15 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: contract_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(contract_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(contract_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size end test "should return the corresponding transactions when page and page_size are set" do - fake_dao_deposit_transaction(30) + address = create(:address) + fake_dao_deposit_transaction(30, address) page = 2 page_size = 5 dao_contract = DaoContract.default_contract @@ -148,7 +153,7 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: contract_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(contract_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(contract_ckb_transactions, options).serialized_json assert_equal response_transaction, response.body end @@ -162,14 +167,15 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: contract_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(contract_ckb_transactions, options).serialized_json + response_transaction = CkbTransactionsSerializer.new(contract_ckb_transactions, options).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body end test "should return pagination links in response body" do - fake_dao_deposit_transaction(30) + address = create(:address) + fake_dao_deposit_transaction(30, address) page = 2 page_size = 3 @@ -187,7 +193,8 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest end test "should return meta that contained total in response body" do - fake_dao_deposit_transaction(3) + address = create(:address) + fake_dao_deposit_transaction(3, address) valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME) @@ -202,18 +209,6 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_contract_transaction_url(DaoContract::CONTRACT_NAME) assert_equal links.stringify_keys.sort, json["links"].sort end - - private - - def fake_dao_deposit_transaction(dao_cell_count) - block = create(:block, :with_block_hash) - dao_cell_count.times do |number| - ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block) - ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block) - generated_by = number % 2 == 0 ? ckb_transaction2 : ckb_transaction1 - create(:cell_output, ckb_transaction: generated_by, cell_index: number, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: generated_by, block: block, capacity: 10**8 * 1000, cell_type: "nervos_dao_deposit") - end - end end end end diff --git a/test/controllers/api/v1/daily_statistics_controller_test.rb b/test/controllers/api/v1/daily_statistics_controller_test.rb index a26e45243..70e5069c5 100644 --- a/test/controllers/api/v1/daily_statistics_controller_test.rb +++ b/test/controllers/api/v1/daily_statistics_controller_test.rb @@ -57,11 +57,11 @@ class DailyStatisticsControllerTest < ActionDispatch::IntegrationTest end test "should return total dao deposit and timestamp" do - daily_statistic_data = create_list(:daily_statistic, 15) + create_list(:daily_statistic, 15) valid_get api_v1_daily_statistic_url("total_dao_deposit") assert_equal [%w(total_dao_deposit created_at_unixtimestamp).sort], json.dig("data").map { |item| item.dig("attributes").keys.sort }.uniq - assert_equal DailyStatisticSerializer.new(daily_statistic_data, params: { indicator: "total_dao_deposit" }).serialized_json, response.body + assert_equal DailyStatisticSerializer.new(DailyStatistic.order(:created_at_unixtimestamp), params: { indicator: "total_dao_deposit" }).serialized_json, response.body end test "should respond with error object when indicator name is invalid" do diff --git a/test/controllers/api/v1/dao_depositors_controller_test.rb b/test/controllers/api/v1/dao_depositors_controller_test.rb index 1e4ba7792..6b1bff9d5 100644 --- a/test/controllers/api/v1/dao_depositors_controller_test.rb +++ b/test/controllers/api/v1/dao_depositors_controller_test.rb @@ -40,7 +40,9 @@ class DaoDepositorsControllerTest < ActionDispatch::IntegrationTest end test "should get serialized dao depositors" do - create_list(:address, 10, dao_deposit: 1000, is_depositor: true) + 10.times do |number| + create(:address, dao_deposit: 1000 + number, is_depositor: true) + end addresses = Address.select(:id, :address_hash, :dao_deposit, :average_deposit_time).where(is_depositor: true).order(dao_deposit: :desc).limit(100) valid_get api_v1_dao_depositors_url diff --git a/test/controllers/api/v1/udt_transactions_controller_test.rb b/test/controllers/api/v1/udt_transactions_controller_test.rb index ece88f3d0..753fc53cf 100644 --- a/test/controllers/api/v1/udt_transactions_controller_test.rb +++ b/test/controllers/api/v1/udt_transactions_controller_test.rb @@ -72,7 +72,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: ckb_transactions, page: page, page_size: page_size).call - assert_equal CkbTransactionSerializer.new(ckb_transactions, options.merge(params: { previews: true })).serialized_json, response.body + assert_equal CkbTransactionsSerializer.new(ckb_transactions, options.merge(params: { previews: true })).serialized_json, response.body end test "should contain right keys in the serialized object when call show" do @@ -82,7 +82,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"].first - assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps).sort, response_tx_transaction["attributes"].keys.sort + assert_equal %w(block_number transaction_hash block_timestamp display_inputs display_outputs is_cellbase income).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by id" do @@ -153,7 +153,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_udt_transaction_url(udt.type_hash), params: { page: page } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: udt_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -168,7 +168,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_udt_transaction_url(udt.type_hash), params: { page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: udt_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body assert_equal page_size, json["data"].size @@ -182,7 +182,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_udt_transaction_url(udt.type_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: udt_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json assert_equal response_transaction, response.body end @@ -196,7 +196,7 @@ class Api::V1::UdtTransactionsControllerTest < ActionDispatch::IntegrationTest valid_get api_v1_udt_transaction_url(udt.type_hash), params: { page: page, page_size: page_size } options = FastJsonapi::PaginationMetaGenerator.new(request: request, records: udt_ckb_transactions, page: page, page_size: page_size).call - response_transaction = CkbTransactionSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json + response_transaction = CkbTransactionsSerializer.new(udt_ckb_transactions, options.merge(params: { previews: true })).serialized_json assert_equal [], json["data"] assert_equal response_transaction, response.body diff --git a/test/factories/address.rb b/test/factories/address.rb index ba49cccf5..d7bfaa684 100644 --- a/test/factories/address.rb +++ b/test/factories/address.rb @@ -36,6 +36,9 @@ after(:create) do |address, evaluator| block = create(:block, :with_block_hash) ckb_transactions = create_list(:ckb_transaction, evaluator.transactions_count, block: block) + ckb_transactions.each do |tx| + tx.contained_address_ids << address.id + end address.ckb_transactions << ckb_transactions end end @@ -45,8 +48,8 @@ after(:create) do |address, evaluator| evaluator.transactions_count.times do block = create(:block, :with_block_hash) - transaction = create(:ckb_transaction, block: block) - transaction1 = create(:ckb_transaction, block: block) + transaction = create(:ckb_transaction, block: block, contained_address_ids: [address.id], tags: ["udt"], contained_udt_ids: [evaluator.udt.id]) + transaction1 = create(:ckb_transaction, block: block, contained_address_ids: [address.id], tags: ["udt"], contained_udt_ids: [evaluator.udt.id]) create(:cell_output, address: address, block: block, ckb_transaction: transaction, generated_by: transaction, consumed_by: transaction1, type_hash: evaluator.udt.type_hash, cell_type: "udt", data: "0x000050ad321ea12e0000000000000000") address.ckb_transactions << transaction address.ckb_transactions << transaction1 diff --git a/test/factories/udt.rb b/test/factories/udt.rb index 2d89d40a8..5cdb0ca47 100644 --- a/test/factories/udt.rb +++ b/test/factories/udt.rb @@ -13,8 +13,8 @@ after(:create) do |udt, _evaluator| 20.times do block = create(:block, :with_block_hash) - transaction = create(:ckb_transaction, block: block) - transaction1 = create(:ckb_transaction, block: block) + transaction = create(:ckb_transaction, block: block, contained_udt_ids: [udt.id], tags: ["udt"]) + transaction1 = create(:ckb_transaction, block: block, contained_udt_ids: [udt.id], tags: ["udt"]) create(:cell_output, block: block, ckb_transaction: transaction, generated_by: transaction, consumed_by: transaction1, type_hash: udt.type_hash, cell_type: "udt", data: "0x000050ad321ea12e0000000000000000") end end diff --git a/test/models/address_test.rb b/test/models/address_test.rb index 7fe12ffb6..491d448a7 100644 --- a/test/models/address_test.rb +++ b/test/models/address_test.rb @@ -109,4 +109,106 @@ class AddressTest < ActiveSupport::TestCase assert_equal (expected_phase1_dao_interests + expected_unmade_dao_interests), address.cal_unclaimed_compensation end + + test "#custom_ckb_transactions should return correct ckb transactions" do + address = create(:address) + block = create(:block) + ckb_transactions = create_list(:ckb_transaction, 30, block: block, address: address, contained_address_ids: [address.id]) + ckb_transactions.each do |tx| + AccountBook.create(address: address, ckb_transaction: tx) + end + + ckb_transaction_ids = address.account_books.select(:ckb_transaction_id).distinct + expected_ckb_transactions = CkbTransaction.where(id: ckb_transaction_ids).recent + + assert_equal expected_ckb_transactions.pluck(:id), address.custom_ckb_transactions.recent.pluck(:id) + end + + test "#ckb_dao_transactions should return correct ckb transactions with dao cell" do + address = create(:address) + address1 = create(:address) + 30.times do |number| + block = create(:block, :with_block_hash) + contained_address_ids = number % 2 == 0 ? [address.id] : [address1.id] + tx = create(:ckb_transaction, block: block, tags: ["dao"], contained_address_ids: contained_address_ids) + AccountBook.create(address: address, ckb_transaction: tx) + cell_type = number % 2 == 0 ? "nervos_dao_deposit" : "nervos_dao_withdrawing" + cell_output_address = number % 2 == 0 ? address : address1 + create(:cell_output, block: block, address: cell_output_address, ckb_transaction: tx, generated_by: tx, cell_type: cell_type) + end + + ckb_transaction_ids = address.cell_outputs.where(cell_type: %w(nervos_dao_deposit nervos_dao_withdrawing)).select("ckb_transaction_id").distinct + expected_ckb_transactions = CkbTransaction.where(id: ckb_transaction_ids).recent + + assert_equal expected_ckb_transactions.pluck(:id), address.ckb_dao_transactions.recent.pluck(:id) + end + + test "#ckb_dao_transactions should return an empty array when there aren't dao cell" do + address = create(:address) + + assert_equal [], address.ckb_dao_transactions.recent.pluck(:id) + end + + test "#ckb_udt_transactions should return correct ckb transactions with udt cell when there are udt cells" do + udt = create(:udt) + address = create(:address) + 30.times do |number| + block = create(:block, :with_block_hash) + if number % 2 == 0 + tx = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + create(:cell_output, block: block, ckb_transaction: tx, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx, address: address) + else + tx = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + tx1 = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + create(:cell_output, block: block, ckb_transaction: tx1, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx1, address: address) + create(:cell_output, block: block, ckb_transaction: tx, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx, consumed_by_id: tx1, address: address) + end + end + + sql = + <<-SQL + SELECT + generated_by_id ckb_transaction_id + FROM + cell_outputs + WHERE + address_id = #{address.id} + AND + cell_type = #{CellOutput::cell_types['udt']} + AND + type_hash = '#{udt.type_hash}' + + UNION + + SELECT + consumed_by_id ckb_transaction_id + FROM + cell_outputs + WHERE + address_id = #{address.id} + AND + cell_type = #{CellOutput::cell_types['udt']} + AND + type_hash = '#{udt.type_hash}' + AND + consumed_by_id is not null + SQL + ckb_transaction_ids = CellOutput.select("ckb_transaction_id").from("(#{sql}) as cell_outputs") + expected_ckb_transactions = CkbTransaction.where(id: ckb_transaction_ids.distinct).recent + + assert_equal expected_ckb_transactions.pluck(:id), address.ckb_udt_transactions(udt.type_hash).recent.pluck(:id) + end + + test "#ckb_udt_transactions should return an empty array when there aren't udt cells" do + udt = create(:udt) + address = create(:address) + + assert_equal [], address.ckb_udt_transactions(udt.type_hash) + end + + test "#ckb_udt_transactions should return an empty array when udt not exist" do + address = create(:address) + + assert_equal [], address.ckb_udt_transactions("0x11") + end end diff --git a/test/models/ckb_sync/node_data_processor_test.rb b/test/models/ckb_sync/node_data_processor_test.rb index 978db03e2..13967e5a3 100644 --- a/test/models/ckb_sync/node_data_processor_test.rb +++ b/test/models/ckb_sync/node_data_processor_test.rb @@ -1491,7 +1491,7 @@ class NodeDataProcessorTest < ActiveSupport::TestCase node_data_processor.process_block(node_block) block = Block.last cellbase = Cellbase.new(block) - expected_cellbase_display_inputs = [CkbUtils.hash_value_to_s({ id: nil, from_cellbase: true, capacity: nil, address_hash: nil, target_block_number: cellbase.target_block_number, generated_tx_hash: block.cellbase.tx_hash })] + expected_cellbase_display_inputs = [CkbUtils.hash_value_to_s(id: nil, from_cellbase: true, capacity: nil, address_hash: nil, target_block_number: cellbase.target_block_number, generated_tx_hash: block.cellbase.tx_hash)] assert_equal expected_cellbase_display_inputs, block.cellbase.display_inputs end @@ -1517,7 +1517,7 @@ class NodeDataProcessorTest < ActiveSupport::TestCase local_block_cell_outputs = local_ckb_transactions.map(&:display_outputs).flatten output = local_ckb_transactions.first.outputs.order(:id).first cellbase = Cellbase.new(local_block) - expected_display_outputs = [CkbUtils.hash_value_to_s({ id: output.id, capacity: output.capacity, address_hash: output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: "live", consumed_tx_hash: nil })] + expected_display_outputs = [CkbUtils.hash_value_to_s(id: output.id, capacity: output.capacity, address_hash: output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: "live", consumed_tx_hash: nil)] assert_equal expected_display_outputs, local_block_cell_outputs end @@ -1550,7 +1550,7 @@ class NodeDataProcessorTest < ActiveSupport::TestCase expected_cellbase_display_outputs = block.cellbase.cell_outputs.order(:id).map do |cell_output| consumed_tx_hash = cell_output.live? ? nil : cell_output.consumed_by.tx_hash - CkbUtils.hash_value_to_s({ id: cell_output.id, capacity: cell_output.capacity, address_hash: cell_output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: cell_output.status, consumed_tx_hash: consumed_tx_hash }) + CkbUtils.hash_value_to_s(id: cell_output.id, capacity: cell_output.capacity, address_hash: cell_output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: cell_output.status, consumed_tx_hash: consumed_tx_hash) end assert_equal expected_cellbase_display_outputs, block.cellbase.display_outputs @@ -1584,7 +1584,7 @@ class NodeDataProcessorTest < ActiveSupport::TestCase block = Block.last cellbase = Cellbase.new(block) cell_output = block.cellbase.cell_outputs.first - expected_cellbase_display_outputs = [CkbUtils.hash_value_to_s({ id: cell_output.id, capacity: cell_output.capacity, address_hash: cell_output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: "live", consumed_tx_hash: nil })] + expected_cellbase_display_outputs = [CkbUtils.hash_value_to_s(id: cell_output.id, capacity: cell_output.capacity, address_hash: cell_output.address_hash, target_block_number: cellbase.target_block_number, base_reward: cellbase.base_reward, commit_reward: cellbase.commit_reward, proposal_reward: cellbase.proposal_reward, secondary_reward: cellbase.secondary_reward, status: "live", consumed_tx_hash: nil)] assert_equal expected_cellbase_display_outputs, block.cellbase.display_outputs end @@ -2068,6 +2068,761 @@ class NodeDataProcessorTest < ActiveSupport::TestCase end end + test "should update tx's contained address ids" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1) + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2) + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block2, capacity: 60000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4) + create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 70000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs1, outputs: outputs1, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + address1 = Address.find_by(lock_hash: lock1.compute_hash) + address2 = Address.find_by(lock_hash: lock2.compute_hash) + address3 = Address.find_by(lock_hash: lock3.compute_hash) + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal [address1.id, address2.id, address3.id, input_address1.id, input_address2.id, input_address3.id], tx.contained_address_ids + assert_equal [address1.id, address2.id, address3.id, input_address4.id, input_address5.id], tx1.contained_address_ids + end + + test "should update tx's tags when output have nervos_dao_deposit cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1) + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2) + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block2, capacity: 70000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4) + create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 70000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + dao_type = CKB::Types::Script.new(code_hash: ENV["DAO_TYPE_HASH"], hash_type: "type", args: "0x") + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %w[0x0000000000000000 0x0000000000000000 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs1, outputs: outputs1, outputs_data: %w[0x0000000000000000 0x0000000000000000 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal ["dao"], tx.tags + assert_equal ["dao"], tx1.tags + end + + test "should update tx's tags when output have nervos_dao_withdrawing cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_deposit") + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "nervos_dao_deposit") + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block2, capacity: 70000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4, cell_type: "nervos_dao_deposit") + create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 70000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5, cell_type: "nervos_dao_deposit") + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + dao_type = CKB::Types::Script.new(code_hash: ENV["DAO_TYPE_HASH"], hash_type: "type", args: "0x") + outputs = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + deposit_block_number = CKB::Utils.bin_to_hex([block1.number].pack("Q<")) + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs, outputs: outputs, outputs_data: %W[#{deposit_block_number} #{deposit_block_number} 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs1, outputs: outputs1, outputs_data: %W[#{deposit_block_number} #{deposit_block_number} 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + assert_equal ["dao"], tx.tags + assert_equal ["dao"], tx1.tags + end + + test "should update tx's tags when input have nervos_dao_withdrawing cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + deposit_block = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 5, dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + deposit_tx = create(:ckb_transaction, block: deposit_block) + deposit_block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 6, dao: "0x185369bb078607007224be7987882300517774e04400000000e3bad4847a0100") + deposit_tx1 = create(:ckb_transaction, block: deposit_block1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:cell_output, ckb_transaction: deposit_tx, generated_by: deposit_tx, block: deposit_block, capacity: 50000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_output, ckb_transaction: deposit_tx, generated_by: deposit_tx, block: deposit_block, capacity: 40000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx.tx_hash, cell_index: 1, address: input_address1, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_input, ckb_transaction: tx1, block: block1, previous_output: { tx_hash: deposit_tx.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx2, block: block2, previous_output: { tx_hash: deposit_tx.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx2, block: block2, previous_output: { tx_hash: deposit_tx.tx_hash, index: 1 }) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_withdrawing", dao: "0x28ef3c7ff3860700d88b1a61958923008ae424cd7200000000e3bad4847a0100") + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "nervos_dao_withdrawing", dao: "0x2cd631702e870700b3df08d7d889230036f787487e00000000e3bad4847a0100") + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + + create(:cell_output, ckb_transaction: deposit_tx1, generated_by: deposit_tx1, block: deposit_block1, capacity: 50000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx1.tx_hash, cell_index: 0, address: input_address4, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_output, ckb_transaction: deposit_tx1, generated_by: deposit_tx1, block: deposit_block1, capacity: 40000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx1.tx_hash, cell_index: 1, address: input_address5, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_input, ckb_transaction: tx4, block: block1, previous_output: { tx_hash: deposit_tx1.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx5, block: block2, previous_output: { tx_hash: deposit_tx1.tx_hash, index: 1 }) + create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block1, capacity: 50000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4, cell_type: "nervos_dao_withdrawing", dao: "0x28ef3c7ff3860700d88b1a61958923008ae424cd7200000000e3bad4847a0100") + create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 60000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5, cell_type: "nervos_dao_withdrawing", dao: "0x2cd631702e870700b3df08d7d889230036f787487e00000000e3bad4847a0100") + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + outputs = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs1, outputs: outputs1, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal ["dao"], tx.tags + assert_equal ["dao"], tx1.tags + end + + test "should update tx's tags when output have udt cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1) + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2) + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + udt_script = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script.args, address_hash: "0x#{SecureRandom.hex(32)}") + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: udt_script), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: udt_script), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %W[#{CKB::Utils.generate_sudt_amount(1000)} #{CKB::Utils.generate_sudt_amount(1000)} 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + + assert_equal ["udt"], tx.tags + end + + test "should update tx's tags when output have udt cells and nervos_dao_deposit cell" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1) + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2) + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + dao_type = CKB::Types::Script.new(code_hash: ENV["DAO_TYPE_HASH"], hash_type: "type", args: "0x") + udt_script = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script.args, address_hash: "0x#{SecureRandom.hex(32)}") + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3, type: udt_script) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %W[0x0000000000000000 0x0000000000000000 #{CKB::Utils.generate_sudt_amount(1000)}], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + + assert_equal %w[dao udt], tx.tags + end + + test "should update tx's tags when output have udt cells and nervos_dao_withdrawing cell" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_deposit") + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "nervos_dao_deposit") + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + udt_script = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script.args, address_hash: "0x#{SecureRandom.hex(32)}") + dao_type = CKB::Types::Script.new(code_hash: ENV["DAO_TYPE_HASH"], hash_type: "type", args: "0x") + outputs = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1, type: dao_type), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2, type: dao_type), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3, type: udt_script) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + deposit_block_number = CKB::Utils.bin_to_hex([block1.number].pack("Q<")) + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs, outputs: outputs, outputs_data: %W[#{deposit_block_number} #{deposit_block_number} #{CKB::Utils.generate_sudt_amount(1000)}], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + assert_equal %w[dao udt], tx.tags + end + + test "should update tx's tags when input have udt cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + address1_lock = create(:lock_script, address: input_address1, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address2_lock = create(:lock_script, address: input_address2, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address3_lock = create(:lock_script, address: input_address3, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address4_lock = create(:lock_script, address: input_address4, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address5_lock = create(:lock_script, address: input_address5, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + output1 = create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "udt", lock_script: address1_lock) + output2 = create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "udt", lock_script: address2_lock) + output3 = create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3, cell_type: "udt", lock_script: address3_lock) + output4 = create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block2, capacity: 70000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4, cell_type: "udt", lock_script: address4_lock) + output5 = create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 70000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5, cell_type: "udt", lock_script: address5_lock) + udt_script = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + udt_script1 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output1) + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output2) + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output3) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output4) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output5) + Address.create(lock_hash: udt_script.args, address_hash: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script1.args, address_hash: "0x#{SecureRandom.hex(32)}") + + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + + outputs1 = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs1, outputs: outputs1, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal ["udt"], tx.tags + assert_equal ["udt"], tx1.tags + end + + test "should update tx's tags when input have udt cells and nervos_dao_withdrawing cells" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + deposit_block = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 5, dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + deposit_tx = create(:ckb_transaction, block: deposit_block) + deposit_block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 6, dao: "0x185369bb078607007224be7987882300517774e04400000000e3bad4847a0100") + deposit_tx1 = create(:ckb_transaction, block: deposit_block1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:lock_script, address: input_address1, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + create(:lock_script, address: input_address2, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + create(:lock_script, address: input_address4, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + create(:lock_script, address: input_address5, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address3_lock = create(:lock_script, address: input_address3, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address4_lock = create(:lock_script, address: input_address4, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + create(:cell_output, ckb_transaction: deposit_tx, generated_by: deposit_tx, block: deposit_block, capacity: 50000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_output, ckb_transaction: deposit_tx, generated_by: deposit_tx, block: deposit_block, capacity: 40000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx.tx_hash, cell_index: 1, address: input_address1, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_input, ckb_transaction: tx1, block: block1, previous_output: { tx_hash: deposit_tx.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx2, block: block2, previous_output: { tx_hash: deposit_tx.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx2, block: block2, previous_output: { tx_hash: deposit_tx.tx_hash, index: 1 }) + output1 = create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "nervos_dao_withdrawing", dao: "0x28ef3c7ff3860700d88b1a61958923008ae424cd7200000000e3bad4847a0100") + output2 = create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "nervos_dao_withdrawing", dao: "0x2cd631702e870700b3df08d7d889230036f787487e00000000e3bad4847a0100") + output3 = create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3, cell_type: "udt", lock_script: address3_lock) + + create(:cell_output, ckb_transaction: deposit_tx1, generated_by: deposit_tx1, block: deposit_block1, capacity: 50000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx1.tx_hash, cell_index: 0, address: input_address4, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_output, ckb_transaction: deposit_tx1, generated_by: deposit_tx1, block: deposit_block1, capacity: 40000 * 10**8, occupied_capacity: 61 * 10**8, tx_hash: deposit_tx1.tx_hash, cell_index: 1, address: input_address5, cell_type: "nervos_dao_deposit", dao: "0x1c3a5eac4286070025e0edf5ca8823001c957f5b5000000000e3bad4847a0100") + create(:cell_input, ckb_transaction: tx4, block: block2, previous_output: { tx_hash: deposit_tx1.tx_hash, index: 0 }) + create(:cell_input, ckb_transaction: tx5, block: block2, previous_output: { tx_hash: deposit_tx1.tx_hash, index: 1 }) + output4 = create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block1, capacity: 50000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4, cell_type: "nervos_dao_withdrawing", dao: "0x28ef3c7ff3860700d88b1a61958923008ae424cd7200000000e3bad4847a0100") + output5 = create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 60000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5, cell_type: "udt", lock_script: address4_lock) + + udt_script = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + udt_script1 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output1) + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output2) + create(:type_script, args: udt_script.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output3) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output4) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output5) + Address.create(lock_hash: udt_script.args, address_hash: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script1.args, address_hash: "0x#{SecureRandom.hex(32)}") + + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + + outputs = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 50000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 60000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [block1.block_hash], inputs: inputs1, outputs: outputs1, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]) + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal %w[dao udt], tx.tags + assert_equal %w[dao udt], tx1.tags + end + + test "#process_block should not update tx's tags when there aren't dao cells and udt cells" do + VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do + node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER) + create(:block, :with_block_hash, number: node_block.header.number - 1) + block = node_data_processor.process_block(node_block) + tags = block.ckb_transactions.pluck(:tags).flatten + + assert_empty tags + end + end + + test "#process_block should not update tx's contained_udt_ids when there aren't udt cells" do + VCR.use_cassette("blocks/#{DEFAULT_NODE_BLOCK_NUMBER}") do + node_block = CkbSync::Api.instance.get_block_by_number(DEFAULT_NODE_BLOCK_NUMBER) + create(:block, :with_block_hash, number: node_block.header.number - 1) + block = node_data_processor.process_block(node_block) + contained_udt_ids = block.ckb_transactions.pluck(:contained_udt_ids).flatten + + assert_empty contained_udt_ids + end + end + + test "#process_block should update tx's contained_udt_ids when there are udt cells in outputs" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1) + create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2) + create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3) + create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block1, capacity: 50000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4) + create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 60000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5) + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)), + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + udt_script1 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + udt_script2 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script1.args, address_hash: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script2.args, address_hash: "0x#{SecureRandom.hex(32)}") + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: udt_script1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: udt_script2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + outputs1 = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1, type: udt_script1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2, type: udt_script2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %W[#{CKB::Utils.generate_sudt_amount(1000)} #{CKB::Utils.generate_sudt_amount(1000)} 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs1, outputs: outputs1, outputs_data: %W[#{CKB::Utils.generate_sudt_amount(1000)} #{CKB::Utils.generate_sudt_amount(1000)} 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + udt1 = Udt.find_by(args: udt_script1.args) + udt2 = Udt.find_by(args: udt_script2.args) + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal [udt1.id, udt2.id], tx.contained_udt_ids + assert_equal [udt1.id, udt2.id], tx1.contained_udt_ids + end + + test "#process_block should update tx's contained_udt_ids when there are udt cells in inputs" do + block1 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 2) + tx1 = create(:ckb_transaction, block: block1) + block2 = create(:block, :with_block_hash, number: DEFAULT_NODE_BLOCK_NUMBER - 1) + tx2 = create(:ckb_transaction, block: block2) + tx3 = create(:ckb_transaction, block: block2) + tx4 = create(:ckb_transaction, block: block2) + tx5 = create(:ckb_transaction, block: block2) + input_address1 = create(:address) + input_address2 = create(:address) + input_address3 = create(:address) + input_address4 = create(:address) + input_address5 = create(:address) + address1_lock = create(:lock_script, address: input_address1, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address2_lock = create(:lock_script, address: input_address2, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address3_lock = create(:lock_script, address: input_address3, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address4_lock = create(:lock_script, address: input_address3, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + address5_lock = create(:lock_script, address: input_address3, args: "0x#{SecureRandom.hex(20)}", code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type") + output1 = create(:cell_output, ckb_transaction: tx1, generated_by: tx1, block: block1, capacity: 50000 * 10**8, tx_hash: tx1.tx_hash, cell_index: 0, address: input_address1, cell_type: "udt", lock_script: address1_lock) + output2 = create(:cell_output, ckb_transaction: tx2, generated_by: tx2, block: block2, capacity: 60000 * 10**8, tx_hash: tx2.tx_hash, cell_index: 1, address: input_address2, cell_type: "udt", lock_script: address2_lock) + output3 = create(:cell_output, ckb_transaction: tx3, generated_by: tx3, block: block2, capacity: 70000 * 10**8, tx_hash: tx3.tx_hash, cell_index: 2, address: input_address3, cell_type: "udt", lock_script: address3_lock) + output4 = create(:cell_output, ckb_transaction: tx4, generated_by: tx4, block: block2, capacity: 70000 * 10**8, tx_hash: tx4.tx_hash, cell_index: 0, address: input_address4, cell_type: "udt", lock_script: address4_lock) + output5 = create(:cell_output, ckb_transaction: tx5, generated_by: tx5, block: block2, capacity: 70000 * 10**8, tx_hash: tx5.tx_hash, cell_index: 0, address: input_address5, cell_type: "udt", lock_script: address5_lock) + udt_script1 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + udt_script2 = CKB::Types::Script.new(code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(32)}") + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output1) + create(:type_script, args: udt_script2.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output2) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output3) + create(:type_script, args: udt_script1.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output4) + create(:type_script, args: udt_script2.args, code_hash: ENV["SUDT_CELL_TYPE_HASH"], hash_type: "data", cell_output: output5) + Address.create(lock_hash: udt_script1.args, address_hash: "0x#{SecureRandom.hex(32)}") + Address.create(lock_hash: udt_script2.args, address_hash: "0x#{SecureRandom.hex(32)}") + + header = CKB::Types::BlockHeader.new(compact_target: "0x1000", hash: "0x#{SecureRandom.hex(32)}", number: DEFAULT_NODE_BLOCK_NUMBER, parent_hash: "0x#{SecureRandom.hex(32)}", nonce: 1757392074788233522, timestamp: CkbUtils.time_in_milliseconds(Time.current), transactions_root: "0x#{SecureRandom.hex(32)}", proposals_hash: "0x#{SecureRandom.hex(32)}", uncles_hash: "0x#{SecureRandom.hex(32)}", version: 0, epoch: 1, dao: "0x01000000000000000000c16ff286230000a3a65e97fd03000057c138586f0000") + inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx1.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx2.tx_hash, index: 1)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx3.tx_hash, index: 2)) + ] + inputs1 = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx4.tx_hash, index: 0)), + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: tx5.tx_hash, index: 0)) + ] + lock1 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock2 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + lock3 = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + + outputs = [ + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock1), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock2), + CKB::Types::Output.new(capacity: 40000 * 10**8, lock: lock3) + ] + miner_lock = CKB::Types::Script.new(code_hash: ENV["SECP_CELL_TYPE_HASH"], hash_type: "type", args: "0x#{SecureRandom.hex(20)}") + cellbase_inputs = [ + CKB::Types::Input.new(previous_output: CKB::Types::OutPoint.new(tx_hash: "0x0000000000000000000000000000000000000000000000000000000000000000", index: 4294967295), since: 3000) + ] + cellbase_outputs = [ + CKB::Types::Output.new(capacity: 200986682127, lock: miner_lock) + ] + transactions = [ + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: cellbase_inputs, outputs: cellbase_outputs, outputs_data: %w[0x], witnesses: ["0x590000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd800000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + CKB::Types::Transaction.new(hash: "0x#{SecureRandom.hex(32)}", cell_deps: [], header_deps: [], inputs: inputs1, outputs: outputs, outputs_data: %w[0x 0x 0x], witnesses: ["0x5d0000000c00000055000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000003954acece65096bfa81258983ddb83915fc56bd804000000123456780000000000000000"]), + ] + node_block = CKB::Types::Block.new(uncles: [], proposals: [], transactions: transactions, header: header) + block = node_data_processor.process_block(node_block) + udt1 = Udt.find_by(args: udt_script1.args) + udt2 = Udt.find_by(args: udt_script2.args) + + tx = block.ckb_transactions.where(is_cellbase: false).first + tx1 = block.ckb_transactions.where(is_cellbase: false).second + + assert_equal [udt1.id, udt2.id], tx.contained_udt_ids + assert_equal [udt1.id, udt2.id], tx1.contained_udt_ids + end + private def node_data_processor diff --git a/test/models/dao_contract_test.rb b/test/models/dao_contract_test.rb index 5671dde43..2336038e9 100644 --- a/test/models/dao_contract_test.rb +++ b/test/models/dao_contract_test.rb @@ -152,4 +152,36 @@ class DaoContractTest < ActiveSupport::TestCase assert_equal 0, dao_contract.claimed_compensation_changes end + + test "#ckb_transactions should return an empty array when there aren't transactions" do + contract = DaoContract.default_contract + + assert_equal [], contract.ckb_transactions + end + + test "#ckb_transactions should return correct transactions when there are dao transactions" do + contract = DaoContract.default_contract + address = create(:address) + address1 = create(:address) + + 30.times do |number| + block = create(:block, :with_block_hash) + cell_type = number % 2 == 0 ? "nervos_dao_deposit" : "nervos_dao_withdrawing" + cell_output_address = number % 2 == 0 ? address : address1 + if number % 2 == 0 + tx = create(:ckb_transaction, block: block, tags: ["dao"]) + create(:cell_output, block: block, address: cell_output_address, ckb_transaction: tx, generated_by: tx, cell_type: cell_type) + else + tx = create(:ckb_transaction, block: block, tags: ["dao"]) + tx1 = create(:ckb_transaction, block: block, tags: ["dao"]) + create(:cell_output, block: block, address: cell_output_address, ckb_transaction: tx1, generated_by: tx1, cell_type: cell_type) + create(:cell_output, block: block, address: cell_output_address, ckb_transaction: tx, generated_by: tx, consumed_by: tx1, cell_type: cell_type) + end + end + + 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 + expected_txs = CkbTransaction.where(id: ckb_transaction_ids.uniq).recent + + assert_equal expected_txs.pluck(:id), contract.ckb_transactions.recent.pluck(:id) + end end diff --git a/test/models/udt_test.rb b/test/models/udt_test.rb index 6c2d68cf4..1670ade08 100644 --- a/test/models/udt_test.rb +++ b/test/models/udt_test.rb @@ -19,4 +19,56 @@ class UdtTest < ActiveSupport::TestCase assert_equal node_type_script, udt.type_script end + + test "#ckb_transactions should return an empty array when there aren't udt cells" do + udt = create(:udt, published: true) + + assert_equal [], udt.ckb_transactions + end + + test "#ckb_transactions should return correct ckb_transactions when there are udt cells under the udt" do + udt = create(:udt) + address = create(:address) + 30.times do |number| + block = create(:block, :with_block_hash) + if number % 2 == 0 + tx = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + create(:cell_output, block: block, ckb_transaction: tx, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx, address: address) + else + tx = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + tx1 = create(:ckb_transaction, block: block, tags: ["udt"], contained_udt_ids: [udt.id], contained_address_ids: [address.id]) + create(:cell_output, block: block, ckb_transaction: tx1, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx1, address: address) + create(:cell_output, block: block, ckb_transaction: tx, cell_type: "udt", type_hash: udt.type_hash, generated_by: tx, consumed_by_id: tx1, address: address) + end + end + + sql = + <<-SQL + SELECT + generated_by_id ckb_transaction_id + FROM + cell_outputs + WHERE + cell_type = #{CellOutput::cell_types['udt']} + AND + type_hash = '#{udt.type_hash}' + + UNION + + SELECT + consumed_by_id ckb_transaction_id + FROM + cell_outputs + WHERE + cell_type = #{CellOutput::cell_types['udt']} + AND + type_hash = '#{udt.type_hash}' + AND + consumed_by_id is not null + SQL + ckb_transaction_ids = CellOutput.select("ckb_transaction_id").from("(#{sql}) as cell_outputs") + expected_txs = CkbTransaction.where(id: ckb_transaction_ids.distinct).recent + + assert_equal expected_txs.pluck(:id), udt.ckb_transactions.recent.pluck(:id) + end end diff --git a/test/services/charts/address_average_deposit_time_generator_test.rb b/test/services/charts/address_average_deposit_time_generator_test.rb new file mode 100644 index 000000000..9ee5f9fdf --- /dev/null +++ b/test/services/charts/address_average_deposit_time_generator_test.rb @@ -0,0 +1,12 @@ +require "test_helper" + +module Charts + class AddressAverageDepositTimeGeneratorTest < ActiveSupport::TestCase + test "should return zero when an address's total deposit is zero" do + addr = create(:address, is_depositor: true) + AddressAverageDepositTimeGenerator.new.perform + + assert_equal 0, addr.reload.average_deposit_time + end + end +end diff --git a/test/services/charts/daily_statistic_generator_test.rb b/test/services/charts/daily_statistic_generator_test.rb index 5358b8d8d..26a8647e3 100644 --- a/test/services/charts/daily_statistic_generator_test.rb +++ b/test/services/charts/daily_statistic_generator_test.rb @@ -36,7 +36,9 @@ class DailyStatisticGeneratorTest < ActiveSupport::TestCase end test "average_block_time should return 840 points" do - create_list(:block_time_statistic, 1000) + 1000.times do |number| + create(:block_time_statistic, stat_timestamp: 35.days.ago.end_of_day.to_i + number) + end daily_generator = Charts::DailyStatisticGenerator.new assert_equal 24 * 35, daily_generator.send(:average_block_time).count end diff --git a/test/test_helper.rb b/test/test_helper.rb index c8dde6ccf..6f58752a6 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -242,6 +242,19 @@ def expected_ranking(address1, address2, address3) ] end +def fake_dao_deposit_transaction(dao_cell_count, address) + block = create(:block, :with_block_hash) + dao_cell_count.times do |number| + if number % 2 == 0 + ckb_transaction1 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block, address: address, contained_address_ids: [address.id], tags: ["dao"]) + create(:cell_output, ckb_transaction: ckb_transaction1, cell_index: number, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction1, block: block, capacity: 10**8 * 1000, cell_type: "nervos_dao_deposit", address: address) + else + ckb_transaction2 = create(:ckb_transaction, tx_hash: "0x#{SecureRandom.hex(32)}", block: block, address: address, contained_address_ids: [address.id], tags: ["dao"]) + create(:cell_output, ckb_transaction: ckb_transaction2, cell_index: number, tx_hash: "0x498315db9c7ba144cca74d2e9122ac9b3a3da1641b2975ae321d91ec34f1c0e3", generated_by: ckb_transaction2, block: block, capacity: 10**8 * 1000, cell_type: "nervos_dao_deposit", address: address) + end + end +end + module RequestHelpers def json JSON.parse(response.body)