diff --git a/Gemfile.lock b/Gemfile.lock index 3634b1966..9f56c050a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,47 +10,47 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + actioncable (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailbox (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.5) - actionpack (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailer (7.0.8.7) + actionpack (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.8.5) - actionview (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionpack (7.0.8.7) + actionview (= 7.0.8.7) + activesupport (= 7.0.8.7) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.5) - actionpack (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actiontext (7.0.8.7) + actionpack (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.5) - activesupport (= 7.0.8.5) + actionview (7.0.8.7) + activesupport (= 7.0.8.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -58,24 +58,24 @@ GEM active_interaction (5.3.0) activemodel (>= 5.2, < 8) activesupport (>= 5.2, < 8) - activejob (7.0.8.5) - activesupport (= 7.0.8.5) + activejob (7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.3.6) - activemodel (7.0.8.5) - activesupport (= 7.0.8.5) - activerecord (7.0.8.5) - activemodel (= 7.0.8.5) - activesupport (= 7.0.8.5) + activemodel (7.0.8.7) + activesupport (= 7.0.8.7) + activerecord (7.0.8.7) + activemodel (= 7.0.8.7) + activesupport (= 7.0.8.7) activerecord-import (1.4.1) activerecord (>= 4.2) - activestorage (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activesupport (= 7.0.8.5) + activestorage (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activesupport (= 7.0.8.7) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.8.5) + activesupport (7.0.8.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -285,7 +285,7 @@ GEM method_source (1.1.0) mini_mime (1.1.5) mini_portile2 (2.8.8) - minitest (5.25.1) + minitest (5.25.4) minitest-reporters (1.6.1) ansi builder @@ -308,12 +308,12 @@ GEM net-protocol newrelic_rpm (8.12.0) nio4r (2.7.3) - nokogiri (1.16.8) + nokogiri (1.17.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.8-arm64-darwin) + nokogiri (1.17.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.8-x86_64-linux) + nokogiri (1.17.1-x86_64-linux) racc (~> 1.4) pagy (5.10.1) activesupport @@ -357,20 +357,20 @@ GEM rack (>= 2.0.0) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8.5) - actioncable (= 7.0.8.5) - actionmailbox (= 7.0.8.5) - actionmailer (= 7.0.8.5) - actionpack (= 7.0.8.5) - actiontext (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activemodel (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + rails (7.0.8.7) + actioncable (= 7.0.8.7) + actionmailbox (= 7.0.8.7) + actionmailer (= 7.0.8.7) + actionpack (= 7.0.8.7) + actiontext (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activemodel (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) bundler (>= 1.15.0) - railties (= 7.0.8.5) + railties (= 7.0.8.7) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -378,9 +378,9 @@ GEM rails-html-sanitizer (1.6.1) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + railties (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) method_source rake (>= 12.2) thor (~> 1.0) diff --git a/app/models/cell_output.rb b/app/models/cell_output.rb index 53eda33ef..eeac7c120 100644 --- a/app/models/cell_output.rb +++ b/app/models/cell_output.rb @@ -107,6 +107,7 @@ class CellOutput < ApplicationRecord where.not(type_hash: nil).or(where.not(data_hash: nil)) } scope :by_scripts, ->(lock_script_ids, type_script_ids) { where("lock_script_id IN (?) OR type_script_id IN (?)", lock_script_ids, type_script_ids) } + scope :established_status, -> { where(status: ["live", "dead"]) } before_create :setup_address diff --git a/app/views/api/v2/scripts/deployed_cells.json.jbuilder b/app/views/api/v2/scripts/deployed_cells.json.jbuilder new file mode 100644 index 000000000..ddf21033d --- /dev/null +++ b/app/views/api/v2/scripts/deployed_cells.json.jbuilder @@ -0,0 +1,29 @@ +json.data do + json.deployed_cells @deployed_cells do |deployed_cell| + json.id deployed_cell.id + json.capacity deployed_cell.capacity + json.ckb_transaction_id deployed_cell.ckb_transaction_id + json.created_at deployed_cell.created_at + json.updated_at deployed_cell.updated_at + json.status deployed_cell.status + json.address_id deployed_cell.address_id + json.block_id deployed_cell.block_id + json.tx_hash deployed_cell.tx_hash + json.cell_index deployed_cell.cell_index + json.consumed_by_id deployed_cell.consumed_by_id + json.cell_type deployed_cell.cell_type + json.data_size deployed_cell.data_size + json.occupied_capacity deployed_cell.occupied_capacity + json.block_timestamp deployed_cell.block_timestamp + json.consumed_block_timestamp deployed_cell.consumed_block_timestamp + json.type_hash deployed_cell.type_hash + json.udt_amount deployed_cell.udt_amount + json.dao deployed_cell.dao + json.lock_script_id deployed_cell.lock_script_id + json.type_script_id deployed_cell.type_script_id + end + json.meta do + json.total @deployed_cells.total_count + json.page_size @page_size.to_i + end +end diff --git a/app/views/api/v2/scripts/referring_cells.json.jbuilder b/app/views/api/v2/scripts/referring_cells.json.jbuilder new file mode 100644 index 000000000..c496c2e9e --- /dev/null +++ b/app/views/api/v2/scripts/referring_cells.json.jbuilder @@ -0,0 +1,29 @@ +json.data do + json.referring_cells @referring_cells do |referring_cell| + json.id referring_cell.id + json.capacity referring_cell.capacity + json.ckb_transaction_id referring_cell.ckb_transaction_id + json.created_at referring_cell.created_at + json.updated_at referring_cell.updated_at + json.status referring_cell.status + json.address_id referring_cell.address_id + json.block_id referring_cell.block_id + json.tx_hash referring_cell.tx_hash + json.cell_index referring_cell.cell_index + json.consumed_by_id referring_cell.consumed_by_id + json.cell_type referring_cell.cell_type + json.data_size referring_cell.data_size + json.occupied_capacity referring_cell.occupied_capacity + json.block_timestamp referring_cell.block_timestamp + json.consumed_block_timestamp referring_cell.consumed_block_timestamp + json.type_hash referring_cell.type_hash + json.udt_amount referring_cell.udt_amount + json.dao referring_cell.dao + json.lock_script_id referring_cell.lock_script_id + json.type_script_id referring_cell.type_script_id + end + json.meta do + json.total @referring_cells.total_count + json.page_size @page_size.to_i + end +end diff --git a/app/workers/address_average_deposit_time_generator.rb b/app/workers/address_average_deposit_time_generator.rb index 2f5fb9321..32b049102 100644 --- a/app/workers/address_average_deposit_time_generator.rb +++ b/app/workers/address_average_deposit_time_generator.rb @@ -22,7 +22,7 @@ def cal_average_deposit_time(address, ended_at = CkbUtils.time_in_milliseconds(T # we must calculate the locked duration from lock to unlock. # for locking deposit cell # we calculate the locked duration from lock to now. - address.cell_outputs.nervos_dao_deposit.each do |cell| + address.cell_outputs.established_status.nervos_dao_deposit.each do |cell| total_deposits += cell.capacity total_ckb_deposit_time += cell.capacity * ((cell.consumed_by_id ? cell.consumed_block_timestamp : ended_at) - cell.block_timestamp) end diff --git a/app/workers/analyze_contract_from_cell_dependency_worker.rb b/app/workers/analyze_contract_from_cell_dependency_worker.rb index a49a95d95..d87544053 100644 --- a/app/workers/analyze_contract_from_cell_dependency_worker.rb +++ b/app/workers/analyze_contract_from_cell_dependency_worker.rb @@ -8,96 +8,158 @@ def perform cell_deps_out_points_attrs = Set.new contract_attrs = Set.new cell_deps_attrs = Set.new + contract_roles = Hash.new { |hash, key| hash[key] = {} } + + # 加载未分析的依赖数据 + dependencies = load_unanalyzed_dependencies + + # 按事务分组并逐组处理 + dependencies.each do |ckb_transaction_id, cell_deps| + ckb_transaction = CkbTransaction.find(ckb_transaction_id) + lock_scripts, type_scripts = analyze_scripts(ckb_transaction) + + process_cell_dependencies( + cell_deps, + lock_scripts, + type_scripts, + cell_deps_out_points_attrs, + contract_attrs, + cell_deps_attrs, + contract_roles, + ) + end - CellDependency.where(contract_analyzed: false).where.not(block_number: nil).order("block_number desc").limit(1000).each do |cell_dep| - cell_deps_attrs << { contract_analyzed: true, ckb_transaction_id: cell_dep.ckb_transaction_id, contract_cell_id: cell_dep.contract_cell_id, dep_type: cell_dep.dep_type } + # 持久化数据 + save_cell_deps_out_points(cell_deps_out_points_attrs) + save_contracts(contract_attrs, contract_roles) + save_cell_dependencies(cell_deps_attrs) + end - next if CellDepsOutPoint.where(contract_cell_id: cell_dep.contract_cell_id).exists? + private - ckb_transaction = CkbTransaction.find(cell_dep.ckb_transaction_id) + # 加载未分析的依赖数据 + def load_unanalyzed_dependencies + CellDependency.where(contract_analyzed: false). + where.not(block_number: nil). + limit(200). + group_by(&:ckb_transaction_id) + end - type_script_hashes = Set.new - lock_script_hashes = Set.new + # 分析脚本 + def analyze_scripts(ckb_transaction) + lock_scripts = {} + type_scripts = {} - cell_outputs = ckb_transaction.cell_outputs.includes(:type_script, :lock_script).to_a - cell_inputs = ckb_transaction.cell_inputs.includes(:previous_cell_output).map(&:previous_cell_output) - cell_inputs.each do |input| - lock_script_hashes << input.lock_script.code_hash - type_script_hashes << input.type_script.code_hash if input.type_script - end + cell_outputs = ckb_transaction.cell_outputs.includes(:type_script).to_a + cell_inputs = ckb_transaction.cell_inputs.includes(:previous_cell_output).map(&:previous_cell_output) - cell_outputs.each do |output| - lock_script_hashes << output.lock_script.code_hash - type_script_hashes << output.type_script.code_hash if output.type_script + (cell_inputs + cell_outputs).each do |cell| + lock_scripts[cell.lock_script.code_hash] = cell.lock_script.hash_type + if cell.type_script + type_scripts[cell.type_script.code_hash] = cell.type_script.hash_type end + end + + [lock_scripts, type_scripts] + end + + # 处理每个依赖 + def process_cell_dependencies(cell_deps, lock_scripts, type_scripts, out_points_attrs, contract_attrs, cell_deps_attrs, contract_roles) + cell_deps.each do |cell_dep| + cell_deps_attrs << { + contract_analyzed: true, + ckb_transaction_id: cell_dep.ckb_transaction_id, + contract_cell_id: cell_dep.contract_cell_id, + dep_type: cell_dep.dep_type, + } + next if Contract.joins(:cell_deps_out_points).where(cell_deps_out_points: { contract_cell_id: cell_dep.contract_cell_id }).exists? case cell_dep.dep_type when "code" - cell_output = cell_dep.cell_output - cell_deps_out_points_attrs << { - tx_hash: cell_output.tx_hash, - cell_index: cell_output.cell_index, - deployed_cell_output_id: cell_output.id, - contract_cell_id: cell_output.id, - } - - is_lock_script = cell_output.type_script&.script_hash.in?(lock_script_hashes) || cell_output.data_hash.in?(lock_script_hashes) - is_type_script = cell_output.type_script&.script_hash.in?(type_script_hashes) || cell_output.data_hash.in?(type_script_hashes) - - if is_lock_script || is_type_script - contract_attrs << - { - type_hash: cell_output.type_script&.script_hash, - data_hash: cell_output.data_hash, - deployed_cell_output_id: cell_output.id, - is_type_script:, - is_lock_script:, - deployed_args: cell_output.type_script&.args, - } - end - + process_code_dep(cell_dep, lock_scripts, type_scripts, out_points_attrs, contract_attrs, contract_roles) when "dep_group" - # when the type of cell_dep is "dep_group", it means the cell specified by the `out_point` is a list of out points to the actual referred contract cells - mid_cell = cell_dep.cell_output - - binary_data = mid_cell.binary_data - # parse the actual list of out points from the data field of the cell - out_points_count = binary_data[0, 4].unpack("L<") - # iterate over the out point list and append actual referred contract cells to cell dependencies_attrs - 0.upto(out_points_count[0] - 1) do |i| - part_tx_hash, cell_index = binary_data[4 + i * 36, 36].unpack("H64L<") - - tx_hash = "0x#{part_tx_hash}" - cell_output = CellOutput.find_by_pointer tx_hash, cell_index - cell_deps_out_points_attrs << { - tx_hash:, - cell_index:, - deployed_cell_output_id: cell_output.id, - contract_cell_id: mid_cell.id, - } - - is_lock_script = cell_output.type_script&.script_hash.in?(lock_script_hashes) || cell_output.data_hash.in?(lock_script_hashes) - is_type_script = cell_output.type_script&.script_hash.in?(type_script_hashes) || cell_output.data_hash.in?(type_script_hashes) - - if is_lock_script || is_type_script - contract_attrs << - { - type_hash: cell_output.type_script&.script_hash, - data_hash: cell_output.data_hash, - deployed_cell_output_id: cell_output.id, - deployed_args: cell_output.type_script&.args, - is_type_script:, - is_lock_script:, - } - end - end + process_dep_group(cell_dep, lock_scripts, type_scripts, out_points_attrs, contract_attrs, contract_roles) end end - if cell_deps_out_points_attrs.any? - CellDepsOutPoint.upsert_all(cell_deps_out_points_attrs, - unique_by: %i[contract_cell_id deployed_cell_output_id]) + end + + # 处理 "code" 类型依赖 + def process_code_dep(cell_dep, lock_scripts, type_scripts, out_points_attrs, contract_attrs, contract_roles) + cell_output = cell_dep.cell_output + out_points_attrs << { + tx_hash: cell_output.tx_hash, + cell_index: cell_output.cell_index, + deployed_cell_output_id: cell_output.id, + contract_cell_id: cell_output.id, + } + + update_contract_roles(cell_output, lock_scripts, type_scripts, contract_roles) + + if contract_roles[cell_output.id][:is_lock_script] || contract_roles[cell_output.id][:is_type_script] + contract_attrs << build_contract_attr(cell_output, lock_scripts, type_scripts) end - Contract.upsert_all(contract_attrs, unique_by: %i[deployed_cell_output_id]) if contract_attrs.any? - CellDependency.upsert_all(cell_deps_attrs, unique_by: %i[ckb_transaction_id contract_cell_id dep_type], update_only: :contract_analyzed) + end + + # 处理 "dep_group" 类型依赖 + def process_dep_group(cell_dep, lock_scripts, type_scripts, out_points_attrs, contract_attrs, contract_roles) + mid_cell = cell_dep.cell_output + binary_data = mid_cell.binary_data + out_points_count = binary_data[0, 4].unpack1("L<") + + 0.upto(out_points_count - 1) do |i| + part_tx_hash, cell_index = binary_data[4 + i * 36, 36].unpack("H64L<") + tx_hash = "0x#{part_tx_hash}" + cell_output = CellOutput.find_by_pointer(tx_hash, cell_index) + + out_points_attrs << { + tx_hash:, + cell_index:, + deployed_cell_output_id: cell_output.id, + contract_cell_id: mid_cell.id, + } + + update_contract_roles(cell_output, lock_scripts, type_scripts, contract_roles) + + if contract_roles[cell_output.id][:is_lock_script] || contract_roles[cell_output.id][:is_type_script] + contract_attrs << build_contract_attr(cell_output, lock_scripts, type_scripts) + end + end + end + + # 更新 contract_roles + def update_contract_roles(cell_output, lock_scripts, type_scripts, contract_roles) + is_lock_script = (lock_scripts[cell_output.data_hash] || lock_scripts[cell_output.type_script&.script_hash]).present? + is_type_script = (type_scripts[cell_output.data_hash] || type_scripts[cell_output.type_script&.script_hash]).present? + data_type = lock_scripts[cell_output.data_hash] || type_scripts[cell_output.data_hash] + + contract_roles[cell_output.id][:is_lock_script] ||= is_lock_script + contract_roles[cell_output.id][:is_type_script] ||= is_type_script + contract_roles[cell_output.id][:hash_type] ||= data_type + end + + # 构建单个合约属性 + def build_contract_attr(cell_output, _lock_scripts, _type_scripts) + { + type_hash: cell_output.type_script&.script_hash, + data_hash: cell_output.data_hash, + deployed_cell_output_id: cell_output.id, + deployed_args: cell_output.type_script&.args, + } + end + + # 保存数据 + def save_cell_deps_out_points(attrs) + CellDepsOutPoint.upsert_all(attrs.to_a, unique_by: %i[contract_cell_id deployed_cell_output_id]) if attrs.any? + end + + def save_contracts(attrs, roles) + return if attrs.empty? + + new_attrs = attrs.map { |attr| attr.merge(roles[attr[:deployed_cell_output_id]]) } + Contract.upsert_all(new_attrs, unique_by: %i[deployed_cell_output_id]) + end + + def save_cell_dependencies(attrs) + CellDependency.upsert_all(attrs.to_a, unique_by: %i[ckb_transaction_id contract_cell_id dep_type], update_only: :contract_analyzed) if attrs.any? end end diff --git a/lib/tasks/migration/analyze_contract_from_start_block.rake b/lib/tasks/migration/analyze_contract_from_start_block.rake index 87fde67a8..53b8cf48f 100644 --- a/lib/tasks/migration/analyze_contract_from_start_block.rake +++ b/lib/tasks/migration/analyze_contract_from_start_block.rake @@ -5,76 +5,46 @@ namespace :migration do cell_deps_out_points_attrs = Set.new contract_attrs = Set.new cell_deps_attrs = Set.new + contract_roles = Hash.new { |hash, key| hash[key] = {} } - CellDependency.where(contract_analyzed: false).where.not(block_number: nil).limit(1000).each do |cell_dep| - cell_deps_attrs << { contract_analyzed: true, ckb_transaction_id: cell_dep.ckb_transaction_id, contract_cell_id: cell_dep.contract_cell_id, dep_type: cell_dep.dep_type } + CellDependency.where(contract_analyzed: false).where.not(block_number: nil).limit(1000).group_by do |cell_dep| + cell_dep.ckb_transaction_id + end.each do |ckb_transaction_id, cell_deps| + ckb_transaction = CkbTransaction.find(ckb_transaction_id) + type_scripts = Hash.new + lock_scripts = Hash.new - next if CellDepsOutPoint.where(contract_cell_id: cell_dep.contract_cell_id).exists? - - ckb_transaction = CkbTransaction.find(cell_dep.ckb_transaction_id) - - type_script_hashes = Set.new - lock_script_hashes = Set.new - - cell_outputs = ckb_transaction.cell_outputs.includes(:type_script, :lock_script).to_a + cell_outputs = ckb_transaction.cell_outputs.includes(:type_script).to_a cell_inputs = ckb_transaction.cell_inputs.includes(:previous_cell_output).map(&:previous_cell_output) cell_inputs.each do |input| - lock_script_hashes << input.lock_script.code_hash - type_script_hashes << input.type_script.code_hash if input.type_script + lock_scripts[input.lock_script.code_hash] = input.lock_script.hash_type + type_scripts[input.type_script.code_hash] = input.type_script.hash_type if input.type_script end - cell_outputs.each do |output| - lock_script_hashes << output.lock_script.code_hash - type_script_hashes << output.type_script.code_hash if output.type_script + lock_scripts[output.lock_script.code_hash] = output.lock_script.hash_type + type_scripts[output.type_script.code_hash] = output.type_script.hash_type if output.type_script end - case cell_dep.dep_type - when "code" - cell_output = cell_dep.cell_output - cell_deps_out_points_attrs << { - tx_hash: cell_output.tx_hash, - cell_index: cell_output.cell_index, - deployed_cell_output_id: cell_output.id, - contract_cell_id: cell_output.id, - } - - is_lock_script = cell_output.type_script&.script_hash.in?(lock_script_hashes) || cell_output.data_hash.in?(lock_script_hashes) - is_type_script = cell_output.type_script&.script_hash.in?(type_script_hashes) || cell_output.data_hash.in?(type_script_hashes) - - if is_lock_script || is_type_script - contract_attrs << - { - type_hash: cell_output.type_script&.script_hash, - data_hash: cell_output.data_hash, - deployed_cell_output_id: cell_output.id, - is_type_script:, - is_lock_script:, - deployed_args: cell_output.type_script&.args, - } - end - - when "dep_group" - # when the type of cell_dep is "dep_group", it means the cell specified by the `out_point` is a list of out points to the actual referred contract cells - mid_cell = cell_dep.cell_output + cell_deps.each do |cell_dep| + cell_deps_attrs << { contract_analyzed: true, ckb_transaction_id: cell_dep.ckb_transaction_id, contract_cell_id: cell_dep.contract_cell_id, dep_type: cell_dep.dep_type } + next if Contract.joins(:cell_deps_out_points).where(cell_deps_out_points: { contract_cell_id: cell_dep.contract_cell_id }).exists? - binary_data = mid_cell.binary_data - # parse the actual list of out points from the data field of the cell - out_points_count = binary_data[0, 4].unpack("L<") - # iterate over the out point list and append actual referred contract cells to cell dependencies_attrs - 0.upto(out_points_count[0] - 1) do |i| - part_tx_hash, cell_index = binary_data[4 + i * 36, 36].unpack("H64L<") - - tx_hash = "0x#{part_tx_hash}" - cell_output = CellOutput.find_by_pointer tx_hash, cell_index + case cell_dep.dep_type + when "code" + cell_output = cell_dep.cell_output cell_deps_out_points_attrs << { - tx_hash:, - cell_index:, + tx_hash: cell_output.tx_hash, + cell_index: cell_output.cell_index, deployed_cell_output_id: cell_output.id, - contract_cell_id: mid_cell.id, + contract_cell_id: cell_output.id, } - is_lock_script = cell_output.type_script&.script_hash.in?(lock_script_hashes) || cell_output.data_hash.in?(lock_script_hashes) - is_type_script = cell_output.type_script&.script_hash.in?(type_script_hashes) || cell_output.data_hash.in?(type_script_hashes) + is_lock_script = (lock_scripts[cell_output.data_hash] || lock_scripts[cell_output.type_script&.script_hash]).present? + is_type_script = (type_scripts[cell_output.data_hash] || type_scripts[cell_output.type_script&.script_hash]).present? + data_type = lock_scripts[cell_output.data_hash] || type_scripts[cell_output.data_hash] + contract_roles[cell_output.id][:is_lock_script] ||= is_lock_script + contract_roles[cell_output.id][:is_type_script] ||= is_type_script + contract_roles[cell_output.id][:hash_type] ||= data_type if is_lock_script || is_type_script contract_attrs << @@ -83,23 +53,65 @@ namespace :migration do data_hash: cell_output.data_hash, deployed_cell_output_id: cell_output.id, deployed_args: cell_output.type_script&.args, - is_type_script:, - is_lock_script:, } end + + when "dep_group" + # when the type of cell_dep is "dep_group", it means the cell specified by the `out_point` is a list of out points to the actual referred contract cells + mid_cell = cell_dep.cell_output + + binary_data = mid_cell.binary_data + # parse the actual list of out points from the data field of the cell + out_points_count = binary_data[0, 4].unpack("L<") + # iterate over the out point list and append actual referred contract cells to cell dependencies_attrs + 0.upto(out_points_count[0] - 1) do |i| + part_tx_hash, cell_index = binary_data[4 + i * 36, 36].unpack("H64L<") + + tx_hash = "0x#{part_tx_hash}" + cell_output = CellOutput.find_by_pointer tx_hash, cell_index + cell_deps_out_points_attrs << { + tx_hash:, + cell_index:, + deployed_cell_output_id: cell_output.id, + contract_cell_id: mid_cell.id, + } + + is_lock_script = (lock_scripts[cell_output.data_hash] || lock_scripts[cell_output.type_script&.script_hash]).present? + is_type_script = (type_scripts[cell_output.data_hash] || type_scripts[cell_output.type_script&.script_hash]).present? + hash_type = lock_scripts[cell_output.data_hash] || type_scripts[cell_output.data_hash] + contract_roles[cell_output.id][:is_lock_script] ||= is_lock_script + contract_roles[cell_output.id][:is_type_script] ||= is_type_script + contract_roles[cell_output.id][:hash_type] ||= hash_type + + if is_lock_script || is_type_script + contract_attrs << + { + type_hash: cell_output.type_script&.script_hash, + data_hash: cell_output.data_hash, + + deployed_cell_output_id: cell_output.id, + deployed_args: cell_output.type_script&.args, + } + end + end end end end - if cell_deps_out_points_attrs.any? CellDepsOutPoint.upsert_all(cell_deps_out_points_attrs, unique_by: %i[contract_cell_id deployed_cell_output_id]) end - Contract.upsert_all(contract_attrs, unique_by: %i[deployed_cell_output_id]) if contract_attrs.any? + # some contract in a cell may be lock script but in another cell may be type script + if contract_attrs.any? + new_contract_attrs = + contract_attrs.map do |attr| + attr.merge(contract_roles[attr[:deployed_cell_output_id]]) + end + puts new_contract_attrs + Contract.upsert_all(new_contract_attrs, unique_by: %i[deployed_cell_output_id]) + end CellDependency.upsert_all(cell_deps_attrs, unique_by: %i[ckb_transaction_id contract_cell_id dep_type], update_only: :contract_analyzed) - puts cell_deps_attrs.to_a.last[:ckb_transaction_id] - break unless CellDependency.where(contract_analyzed: false).where.not(block_number: nil).exists? end puts "DONE"