From 495503bf8f7b3ac61b380f4b5b4e5540f4e9b202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:38:35 +0900 Subject: [PATCH 1/2] chore(deps): bump rails-html-sanitizer from 1.6.0 to 1.6.1 (#2305) Bumps [rails-html-sanitizer](https://github.com/rails/rails-html-sanitizer) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/rails/rails-html-sanitizer/releases) - [Changelog](https://github.com/rails/rails-html-sanitizer/blob/main/CHANGELOG.md) - [Commits](https://github.com/rails/rails-html-sanitizer/compare/v1.6.0...v1.6.1) --- updated-dependencies: - dependency-name: rails-html-sanitizer dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b59939032..c7c4b930e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -273,7 +273,7 @@ GEM llhttp-ffi (0.4.0) ffi-compiler (~> 1.0) rake (~> 13.0) - loofah (2.22.0) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -284,7 +284,7 @@ GEM marcel (1.0.4) method_source (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.7) + mini_portile2 (2.8.8) minitest (5.25.1) minitest-reporters (1.6.1) ansi @@ -308,12 +308,12 @@ GEM net-protocol newrelic_rpm (8.12.0) nio4r (2.7.3) - nokogiri (1.16.7) + nokogiri (1.16.8) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.16.8-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.16.8-x86_64-linux) racc (~> 1.4) pagy (5.10.1) activesupport @@ -375,9 +375,9 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.1) loofah (~> 2.21) - nokogiri (~> 1.14) + 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) From 6c9ce5cce1fa628649eeb57a5538804b4efb3845 Mon Sep 17 00:00:00 2001 From: Rabbit Date: Thu, 5 Dec 2024 16:00:40 +0800 Subject: [PATCH 2/2] Feat/fiber (#2301) * feat: synchronize peer and channel data in fiber network * feat: add fiber coordinator * chore: add foreign key fiber_peer_id * chore: rename sent_tlc_balance column * chore: refine fiber channel jbuilder attribute definitions * feat: fiber peers paginate * chore: change rpc_listening_addr type to array * feat: sync fiber graph nodes and channels * fix: set default page_size for fiber request * feat: sync graph node udt config info * chore: fiber udt cfg info add type_script * refactor: fiber graph detect worker * chore: add filter to fiber graph channels * feat: add filter query to fiber graph node * refactor: normalize query_key based on hex string detection * feat: fiber graph channel add closed_transaction_info --- .env.example | 3 + .../api/v2/fiber/channels_controller.rb | 12 + .../api/v2/fiber/graph_channels_controller.rb | 17 + .../api/v2/fiber/graph_nodes_controller.rb | 31 ++ .../api/v2/fiber/peers_controller.rb | 46 ++ .../api/v2/portfolio/addresses_controller.rb | 2 +- app/lib/api/v2/exceptions.rb | 30 +- app/models/fiber_channel.rb | 35 ++ app/models/fiber_graph_channel.rb | 75 +++ app/models/fiber_graph_node.rb | 50 ++ app/models/fiber_peer.rb | 38 ++ app/models/fiber_transaction.rb | 16 + app/models/fiber_udt_cfg_info.rb | 24 + app/models/referring_cell.rb | 1 + app/models/suggest_query.rb | 14 + app/serializers/fiber_channel_serializer.rb | 7 + .../fiber_graph_node_serializer.rb | 6 + app/services/fiber_coordinator.rb | 41 ++ app/views/api/v2/fiber/channels/show.jbuilder | 11 + .../v2/fiber/graph_channels/index.jbuilder | 17 + .../api/v2/fiber/graph_nodes/index.jbuilder | 13 + .../api/v2/fiber/graph_nodes/show.jbuilder | 18 + app/views/api/v2/fiber/peers/index.jbuilder | 10 + app/views/api/v2/fiber/peers/show.jbuilder | 9 + app/workers/fiber_detect_worker.rb | 49 ++ app/workers/fiber_graph_detect_worker.rb | 122 +++++ config/routes/v2.rb | 6 + config/settings.mainnet.yml | 3 + config/settings.testnet.yml | 3 + config/sidekiq.yml | 1 + .../20240918024407_create_fiber_peers.rb | 13 + .../20240918024415_create_fiber_channels.rb | 19 + ...0240918033146_create_fiber_transactions.rb | 11 + ...807_add_fiber_peer_id_to_fiber_channels.rb | 7 + ...5539_update_rpc_listening_addr_to_array.rb | 5 + ...20241012014906_create_fiber_graph_infos.rb | 33 ++ ...241023055256_create_fiber_udt_cfg_infos.rb | 13 + ...3536_add_udt_id_to_fiber_graph_channels.rb | 5 + ..._transaction_id_to_fiber_graph_channels.rb | 8 + ...032447_add_peer_id_to_fiber_graph_nodes.rb | 5 + db/structure.sql | 478 ++++++++++++++++-- lib/scheduler.rb | 4 + test/factories/fiber_channels.rb | 5 + test/factories/fiber_graph_channels.rb | 5 + test/factories/fiber_graph_nodes.rb | 5 + test/factories/fiber_peers.rb | 5 + test/factories/fiber_udt_cfg_infos.rb | 5 + test/models/fiber_channel_test.rb | 7 + test/models/fiber_graph_channel_test.rb | 7 + test/models/fiber_graph_node_test.rb | 7 + test/models/fiber_peer_test.rb | 7 + test/models/fiber_udt_cfg_info_test.rb | 7 + 52 files changed, 1334 insertions(+), 37 deletions(-) create mode 100644 app/controllers/api/v2/fiber/channels_controller.rb create mode 100644 app/controllers/api/v2/fiber/graph_channels_controller.rb create mode 100644 app/controllers/api/v2/fiber/graph_nodes_controller.rb create mode 100644 app/controllers/api/v2/fiber/peers_controller.rb create mode 100644 app/models/fiber_channel.rb create mode 100644 app/models/fiber_graph_channel.rb create mode 100644 app/models/fiber_graph_node.rb create mode 100644 app/models/fiber_peer.rb create mode 100644 app/models/fiber_transaction.rb create mode 100644 app/models/fiber_udt_cfg_info.rb create mode 100644 app/serializers/fiber_channel_serializer.rb create mode 100644 app/serializers/fiber_graph_node_serializer.rb create mode 100644 app/services/fiber_coordinator.rb create mode 100644 app/views/api/v2/fiber/channels/show.jbuilder create mode 100644 app/views/api/v2/fiber/graph_channels/index.jbuilder create mode 100644 app/views/api/v2/fiber/graph_nodes/index.jbuilder create mode 100644 app/views/api/v2/fiber/graph_nodes/show.jbuilder create mode 100644 app/views/api/v2/fiber/peers/index.jbuilder create mode 100644 app/views/api/v2/fiber/peers/show.jbuilder create mode 100644 app/workers/fiber_detect_worker.rb create mode 100644 app/workers/fiber_graph_detect_worker.rb create mode 100644 db/migrate/20240918024407_create_fiber_peers.rb create mode 100644 db/migrate/20240918024415_create_fiber_channels.rb create mode 100644 db/migrate/20240918033146_create_fiber_transactions.rb create mode 100644 db/migrate/20240920094807_add_fiber_peer_id_to_fiber_channels.rb create mode 100644 db/migrate/20240924065539_update_rpc_listening_addr_to_array.rb create mode 100644 db/migrate/20241012014906_create_fiber_graph_infos.rb create mode 100644 db/migrate/20241023055256_create_fiber_udt_cfg_infos.rb create mode 100644 db/migrate/20241023063536_add_udt_id_to_fiber_graph_channels.rb create mode 100644 db/migrate/20241030023309_add_transaction_id_to_fiber_graph_channels.rb create mode 100644 db/migrate/20241129032447_add_peer_id_to_fiber_graph_nodes.rb create mode 100644 test/factories/fiber_channels.rb create mode 100644 test/factories/fiber_graph_channels.rb create mode 100644 test/factories/fiber_graph_nodes.rb create mode 100644 test/factories/fiber_peers.rb create mode 100644 test/factories/fiber_udt_cfg_infos.rb create mode 100644 test/models/fiber_channel_test.rb create mode 100644 test/models/fiber_graph_channel_test.rb create mode 100644 test/models/fiber_graph_node_test.rb create mode 100644 test/models/fiber_peer_test.rb create mode 100644 test/models/fiber_udt_cfg_info_test.rb diff --git a/.env.example b/.env.example index fe6ed13b7..a468ee942 100644 --- a/.env.example +++ b/.env.example @@ -127,3 +127,6 @@ BITCOIN_SIGNET_PASS="" # Dynamic CORS configuration PARTNER_DOMAINS="/localhost:\d*/" + +# -------------------------------- Fiber segment -------------------------------- +FIBER_NODE_URL="" \ No newline at end of file diff --git a/app/controllers/api/v2/fiber/channels_controller.rb b/app/controllers/api/v2/fiber/channels_controller.rb new file mode 100644 index 000000000..bb21847dc --- /dev/null +++ b/app/controllers/api/v2/fiber/channels_controller.rb @@ -0,0 +1,12 @@ +module Api + module V2 + module Fiber + class ChannelsController < BaseController + def show + @channel = FiberChannel.find_by(channel_id: params[:channel_id]) + raise Api::V2::Exceptions::FiberChannelNotFoundError unless @channel + end + end + end + end +end diff --git a/app/controllers/api/v2/fiber/graph_channels_controller.rb b/app/controllers/api/v2/fiber/graph_channels_controller.rb new file mode 100644 index 000000000..59fee4c0b --- /dev/null +++ b/app/controllers/api/v2/fiber/graph_channels_controller.rb @@ -0,0 +1,17 @@ +module Api + module V2 + module Fiber + class GraphChannelsController < BaseController + def index + @page = params.fetch(:page, 1) + @page_size = params.fetch(:page_size, FiberPeer.default_per_page) + @channels = FiberGraphChannel.all + if params[:status] == "closed" + @channels = @channels.where.not(closed_transaction_id: nil) + end + @channels = @channels.page(@page).per(@page_size) + end + end + end + end +end diff --git a/app/controllers/api/v2/fiber/graph_nodes_controller.rb b/app/controllers/api/v2/fiber/graph_nodes_controller.rb new file mode 100644 index 000000000..2c175c037 --- /dev/null +++ b/app/controllers/api/v2/fiber/graph_nodes_controller.rb @@ -0,0 +1,31 @@ +module Api + module V2 + module Fiber + class GraphNodesController < BaseController + def index + @page = params.fetch(:page, 1) + @page_size = params.fetch(:page_size, FiberGraphNode.default_per_page) + @nodes = + if params[:q].present? + FiberGraphNode.where("alias = :q or peer_id = :q or node_id = :q", q: params[:q]).page(@page).per(@page_size) + else + FiberGraphNode.all.page(@page).per(@page_size) + end + end + + def show + @node = FiberGraphNode.find_by(node_id: params[:node_id]) + raise Api::V2::Exceptions::FiberGraphNodeNotFoundError unless @node + + @graph_channels = FiberGraphChannel.where(node1: params[:node_id]).or( + FiberGraphChannel.where(node2: params[:node_id]), + ) + + if params[:status] == "closed" + @graph_channels = @graph_channels.where.not(closed_transaction_id: nil) + end + end + end + end + end +end diff --git a/app/controllers/api/v2/fiber/peers_controller.rb b/app/controllers/api/v2/fiber/peers_controller.rb new file mode 100644 index 000000000..fb1584b01 --- /dev/null +++ b/app/controllers/api/v2/fiber/peers_controller.rb @@ -0,0 +1,46 @@ +module Api + module V2 + module Fiber + class PeersController < BaseController + before_action :test_connection, only: :create + + def index + @page = params.fetch(:page, 1) + @page_size = params.fetch(:page_size, FiberPeer.default_per_page) + @peers = FiberPeer.all.page(@page).per(@page_size) + end + + def show + @peer = FiberPeer.find_by(peer_id: params[:peer_id]) + raise Api::V2::Exceptions::FiberPeerNotFoundError unless @peer + end + + def create + fiber_peer = FiberPeer.find_or_initialize_by(peer_id: fiber_peer_params[:peer_id]) + fiber_peer.name = fiber_peer_params[:name] + new_rpc = Array(fiber_peer_params[:rpc_listening_addr]) + fiber_peer.rpc_listening_addr = (fiber_peer.rpc_listening_addr + new_rpc).uniq + fiber_peer.save! + + FiberDetectWorker.perform_async(fiber_peer.peer_id) + + head :no_content + rescue ActiveRecord::RecordInvalid => e + raise Api::V2::Exceptions::FiberPeerParamsInvalidError.new(e.message) + end + + private + + def fiber_peer_params + params.permit(:name, :peer_id, :rpc_listening_addr) + end + + def test_connection + FiberCoordinator.instance.list_channels(fiber_peer_params[:rpc_listening_addr], { "peer_id": nil }) + rescue StandardError => e + raise Api::V2::Exceptions::FiberPeerParamsInvalidError.new(e.message) + end + end + end + end +end diff --git a/app/controllers/api/v2/portfolio/addresses_controller.rb b/app/controllers/api/v2/portfolio/addresses_controller.rb index 3925d326b..37d5b20fd 100644 --- a/app/controllers/api/v2/portfolio/addresses_controller.rb +++ b/app/controllers/api/v2/portfolio/addresses_controller.rb @@ -10,7 +10,7 @@ def create head :no_content rescue StandardError => e - raise Api::V2::Exceptions::SyncPortfolioAddressesError + raise Api::V2::Exceptions::ParamsInvalidError end end end diff --git a/app/lib/api/v2/exceptions.rb b/app/lib/api/v2/exceptions.rb index 857785075..c33080b83 100644 --- a/app/lib/api/v2/exceptions.rb +++ b/app/lib/api/v2/exceptions.rb @@ -21,13 +21,13 @@ def initialize(detail) class TokenCollectionNotFoundError < Error def initialize - super(code: 2001, status: 404, title: "Token Collection Not Found", detail: "No token collection found by given script hash or id", href: "") + super(code: 2001, status: 404, title: "token collection not found", detail: "No token collection found by given script hash or id", href: "") end end class AddressNotMatchEnvironmentError < Error def initialize(ckb_net_mode) - super(code: 2022, status: 422, title: "Address is invalid", detail: "This address is not the #{ckb_net_mode} address", href: "") + super(code: 2022, status: 422, title: "address is invalid", detail: "This address is not the #{ckb_net_mode} address", href: "") end end @@ -69,7 +69,31 @@ def initialize class AddressNotFoundError < Error def initialize - super code: 2009, status: 404, title: "Address Not Found", detail: "No address found by given address hash or lock hash", href: "" + super code: 2009, status: 404, title: "address not found", detail: "No address found by given address hash or lock hash", href: "" + end + end + + class FiberPeerParamsInvalidError < Error + def initialize(detail) + super code: 2010, status: 404, title: "fiber peer params invalid", detail: , href: "" + end + end + + class FiberPeerNotFoundError < Error + def initialize + super code: 2011, status: 404, title: "fiber peer not found", detail: "No peer found by given peer id", href: "" + end + end + + class FiberChannelNotFoundError < Error + def initialize + super code: 2012, status: 404, title: "fiber channel not found", detail: "No channel found by given channel_id", href: "" + end + end + + class FiberGraphNodeNotFoundError < Error + def initialize + super code: 2013, status: 404, title: "fiber graph node not found", detail: "No graph node found by given node id", href: "" end end end diff --git a/app/models/fiber_channel.rb b/app/models/fiber_channel.rb new file mode 100644 index 000000000..6c6d98c3c --- /dev/null +++ b/app/models/fiber_channel.rb @@ -0,0 +1,35 @@ +class FiberChannel < ApplicationRecord + belongs_to :fiber_peer + + def local_peer + fiber_peer || FiberPeer.new + end + + def remote_peer + FiberPeer.find_by(peer_id:) || FiberPeer.new + end +end + +# == Schema Information +# +# Table name: fiber_channels +# +# id :bigint not null, primary key +# peer_id :string +# channel_id :string +# state_name :string +# state_flags :string default([]), is an Array +# local_balance :decimal(64, 2) default(0.0) +# offered_tlc_balance :decimal(64, 2) default(0.0) +# remote_balance :decimal(64, 2) default(0.0) +# received_tlc_balance :decimal(64, 2) default(0.0) +# shutdown_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# fiber_peer_id :integer +# +# Indexes +# +# index_fiber_channels_on_fiber_peer_id (fiber_peer_id) +# index_fiber_channels_on_peer_id_and_channel_id (peer_id,channel_id) UNIQUE +# diff --git a/app/models/fiber_graph_channel.rb b/app/models/fiber_graph_channel.rb new file mode 100644 index 000000000..42e03ee64 --- /dev/null +++ b/app/models/fiber_graph_channel.rb @@ -0,0 +1,75 @@ +class FiberGraphChannel < ApplicationRecord + MAX_PAGINATES_PER = 100 + DEFAULT_PAGINATES_PER = 10 + paginates_per DEFAULT_PAGINATES_PER + max_paginates_per MAX_PAGINATES_PER + + belongs_to :udt, optional: true + belongs_to :open_transaction, class_name: "CkbTransaction" + belongs_to :closed_transaction, class_name: "CkbTransaction", optional: true + + validates :open_transaction_id, presence: true + + scope :open_channels, -> { where(closed_transaction_id: nil) } + + def open_transaction_info + open_transaction.as_json(only: %i[tx_hash block_number block_timestamp]).merge( + { + capacity: funding_cell.capacity, + udt_amount: funding_cell.udt_amount, + address: funding_cell.address_hash, + }, + ) + end + + def closed_transaction_info + return Hash.new unless closed_transaction + + closed_transaction.as_json(only: %i[tx_hash block_number block_timestamp]).merge( + close_accounts: closed_transaction.outputs.map do |cell| + { + capacity: cell.capacity, + udt_amount: cell.udt_amount, + address: cell.address_hash, + } + end, + ) + end + + def udt_info + udt&.as_json(only: %i[full_name symbol decimal icon_file]) + end + + def funding_cell + open_transaction.outputs.includes(:lock_script).find_by( + lock_scripts: { code_hash: Settings.fiber_funding_code_hash }, + ) + end +end + +# == Schema Information +# +# Table name: fiber_graph_channels +# +# id :bigint not null, primary key +# channel_outpoint :string +# funding_tx_block_number :bigint +# funding_tx_index :integer +# node1 :string +# node2 :string +# last_updated_timestamp :bigint +# created_timestamp :bigint +# node1_to_node2_fee_rate :decimal(30, ) default(0) +# node2_to_node1_fee_rate :decimal(30, ) default(0) +# capacity :decimal(64, 2) default(0.0) +# chain_hash :string +# created_at :datetime not null +# updated_at :datetime not null +# udt_id :bigint +# open_transaction_id :bigint +# closed_transaction_id :bigint +# +# Indexes +# +# index_fiber_graph_channels_on_channel_outpoint (channel_outpoint) UNIQUE +# diff --git a/app/models/fiber_graph_node.rb b/app/models/fiber_graph_node.rb new file mode 100644 index 000000000..4b39f8496 --- /dev/null +++ b/app/models/fiber_graph_node.rb @@ -0,0 +1,50 @@ +class FiberGraphNode < ApplicationRecord + MAX_PAGINATES_PER = 100 + DEFAULT_PAGINATES_PER = 10 + paginates_per DEFAULT_PAGINATES_PER + max_paginates_per MAX_PAGINATES_PER + + has_many :fiber_udt_cfg_infos, dependent: :delete_all + + def channel_links + FiberGraphChannel.where(node1: node_id).or(FiberGraphChannel.where(node2: node_id)). + where(closed_transaction_id: nil) + end + + def udt_cfg_infos + fiber_udt_cfg_infos.map(&:udt_info) + end + + def total_capacity + channel_links.sum(&:capacity) + end + + def connected_node_ids + node_ids = channel_links.pluck(:node1, :node2).flatten + node_ids.uniq - [node_id] + end + + def open_channels_count + channel_links.count + end +end + +# == Schema Information +# +# Table name: fiber_graph_nodes +# +# id :bigint not null, primary key +# alias :string +# node_id :string +# addresses :string default([]), is an Array +# timestamp :bigint +# chain_hash :string +# auto_accept_min_ckb_funding_amount :decimal(30, ) +# created_at :datetime not null +# updated_at :datetime not null +# peer_id :string +# +# Indexes +# +# index_fiber_graph_nodes_on_node_id (node_id) UNIQUE +# diff --git a/app/models/fiber_peer.rb b/app/models/fiber_peer.rb new file mode 100644 index 000000000..616e3c1d0 --- /dev/null +++ b/app/models/fiber_peer.rb @@ -0,0 +1,38 @@ +class FiberPeer < ApplicationRecord + MAX_PAGINATES_PER = 100 + DEFAULT_PAGINATES_PER = 10 + paginates_per DEFAULT_PAGINATES_PER + max_paginates_per MAX_PAGINATES_PER + + has_many :fiber_channels, dependent: :destroy + + validates :peer_id, presence: true, uniqueness: true + + def total_local_balance + fiber_channels.where(state_name: "CHANNEL_READY").sum(:local_balance) + end + + def channels_count + fiber_channels.where(state_name: "CHANNEL_READY").count + end +end + +# == Schema Information +# +# Table name: fiber_peers +# +# id :bigint not null, primary key +# name :string +# peer_id :string +# rpc_listening_addr :string default([]), is an Array +# first_channel_opened_at :datetime +# last_channel_updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# node_id :string +# chain_hash :string +# +# Indexes +# +# index_fiber_peers_on_peer_id (peer_id) UNIQUE +# diff --git a/app/models/fiber_transaction.rb b/app/models/fiber_transaction.rb new file mode 100644 index 000000000..4660675df --- /dev/null +++ b/app/models/fiber_transaction.rb @@ -0,0 +1,16 @@ +class FiberTransaction < ApplicationRecord + belongs_to :fiber_channel + belongs_to :fiber_peer +end + +# == Schema Information +# +# Table name: fiber_transactions +# +# id :bigint not null, primary key +# fiber_peer_id :integer +# fiber_channel_id :integer +# ckb_transaction_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# diff --git a/app/models/fiber_udt_cfg_info.rb b/app/models/fiber_udt_cfg_info.rb new file mode 100644 index 000000000..fb5d9b8d6 --- /dev/null +++ b/app/models/fiber_udt_cfg_info.rb @@ -0,0 +1,24 @@ +class FiberUdtCfgInfo < ApplicationRecord + belongs_to :fiber_graph_node + belongs_to :udt + + def udt_info + udt.as_json(only: %i[full_name symbol decimal icon_file args code_hash hash_type]).merge(auto_accept_amount:) + end +end + +# == Schema Information +# +# Table name: fiber_udt_cfg_infos +# +# id :bigint not null, primary key +# fiber_graph_node_id :bigint +# udt_id :bigint +# auto_accept_amount :decimal(64, 2) default(0.0) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_fiber_udt_cfg_infos_on_fiber_graph_node_id_and_udt_id (fiber_graph_node_id,udt_id) UNIQUE +# diff --git a/app/models/referring_cell.rb b/app/models/referring_cell.rb index 9ae25ebf2..2deaba7d1 100644 --- a/app/models/referring_cell.rb +++ b/app/models/referring_cell.rb @@ -56,5 +56,6 @@ def self.create_initial_data_for_ckb_transaction(ckb_transaction) # # Indexes # +# index_referring_cells_on_cell_output_id (cell_output_id) UNIQUE # index_referring_cells_on_contract_id_and_cell_output_id (contract_id,cell_output_id) UNIQUE # diff --git a/app/models/suggest_query.rb b/app/models/suggest_query.rb index e61740cda..b004e963f 100644 --- a/app/models/suggest_query.rb +++ b/app/models/suggest_query.rb @@ -57,6 +57,9 @@ def aggregate_query! if (collections = find_nft_collections_by_name).present? results[:data].concat(collections.serializable_hash[:data]) end + if (nodes = find_fiber_graph_nodes).present? + results[:data].concat(nodes.serializable_hash[:data]) + end raise ActiveRecord::RecordNotFound if results.blank? @@ -152,4 +155,15 @@ def find_bitcoin_address bitcoin_address = BitcoinAddress.find_by(address_hash: query_key) BitcoinAddressSerializer.new(bitcoin_address) if bitcoin_address.present? end + + def find_fiber_graph_nodes + normalized_key = + if QueryKeyUtils.start_with_default_hash_prefix?(query_key) + query_key.delete_prefix(Settings.default_hash_prefix) + else + query_key + end + fiber_graph_nodes = FiberGraphNode.where("alias = :query_key or peer_id = :query_key or node_id = :query_key", query_key: normalized_key) + FiberGraphNodeSerializer.new(fiber_graph_nodes) if fiber_graph_nodes.present? + end end diff --git a/app/serializers/fiber_channel_serializer.rb b/app/serializers/fiber_channel_serializer.rb new file mode 100644 index 000000000..2262ef5a6 --- /dev/null +++ b/app/serializers/fiber_channel_serializer.rb @@ -0,0 +1,7 @@ +class FiberChannelSerializer + include FastJsonapi::ObjectSerializer + + attributes :peer_id, :channel_id, :state_name, :state_flags, :local_balance, + :sent_tlc_balance, :remote_balance, :received_tlc_balance, :shutdown_at, + :created_at, :updated_at +end diff --git a/app/serializers/fiber_graph_node_serializer.rb b/app/serializers/fiber_graph_node_serializer.rb new file mode 100644 index 000000000..a191d52ee --- /dev/null +++ b/app/serializers/fiber_graph_node_serializer.rb @@ -0,0 +1,6 @@ +class FiberGraphNodeSerializer + include FastJsonapi::ObjectSerializer + + attributes :id, :alias, :node_id, :peer_id, :addresses, :timestamp, :chain_hash, + :auto_accept_min_ckb_funding_amount +end diff --git a/app/services/fiber_coordinator.rb b/app/services/fiber_coordinator.rb new file mode 100644 index 000000000..a8f143a9a --- /dev/null +++ b/app/services/fiber_coordinator.rb @@ -0,0 +1,41 @@ +class FiberCoordinator + include Singleton + METHOD_NAMES = %w(graph_nodes graph_channels list_channels).freeze + + def initialize + @id = 0 + end + + METHOD_NAMES.each do |name| + define_method name do |endpoint, *params| + call_rpc(name, endpoint, params:) + end + end + + private + + def call_rpc(method, endpoint, params: []) + @id += 1 + payload = { jsonrpc: "2.0", id: @id, method:, params: } + make_request(endpoint, payload) + end + + def make_request(endpoint, payload) + response = HTTP.timeout(60).post(endpoint, json: payload) + parse_response(response) + end + + def parse_response(response) + data = JSON.parse(response.to_s) + + return data if data.is_a?(Array) + + if data.is_a?(Hash) + raise ArgumentError, data["error"]["data"] if data["error"].present? + else + raise ArgumentError, "Unexpected response format: #{data.class}" + end + + data + end +end diff --git a/app/views/api/v2/fiber/channels/show.jbuilder b/app/views/api/v2/fiber/channels/show.jbuilder new file mode 100644 index 000000000..570f174f0 --- /dev/null +++ b/app/views/api/v2/fiber/channels/show.jbuilder @@ -0,0 +1,11 @@ +json.data do + json.(@channel, :channel_id, :state_name, :state_flags, :shutdown_at, :created_at, :updated_at, :local_balance, :offered_tlc_balance, :remote_balance, :received_tlc_balance) + + json.local_peer do + json.(@channel.local_peer, :peer_id, :name, :rpc_listening_addr) + end + + json.remote_peer do + json.(@channel.remote_peer, :peer_id, :name, :rpc_listening_addr) + end +end diff --git a/app/views/api/v2/fiber/graph_channels/index.jbuilder b/app/views/api/v2/fiber/graph_channels/index.jbuilder new file mode 100644 index 000000000..a53cbb927 --- /dev/null +++ b/app/views/api/v2/fiber/graph_channels/index.jbuilder @@ -0,0 +1,17 @@ +json.data do + json.fiber_graph_channels @channels do |channel| + json.(channel, :channel_outpoint, :node1, :node2, :chain_hash, :open_transaction_info, :closed_transaction_info) + json.funding_tx_block_number channel.funding_tx_block_number.to_s + json.funding_tx_index channel.funding_tx_index.to_s + json.last_updated_timestamp channel.last_updated_timestamp.to_s + json.created_timestamp channel.created_timestamp.to_s + json.node1_to_node2_fee_rate channel.node1_to_node2_fee_rate.to_s + json.node2_to_node1_fee_rate channel.node2_to_node1_fee_rate.to_s + json.capacity channel.capacity.to_s + json.udt_cfg_info channel.udt_info + end +end +json.meta do + json.total @channels.total_count + json.page_size @page_size.to_i +end diff --git a/app/views/api/v2/fiber/graph_nodes/index.jbuilder b/app/views/api/v2/fiber/graph_nodes/index.jbuilder new file mode 100644 index 000000000..a0140cad1 --- /dev/null +++ b/app/views/api/v2/fiber/graph_nodes/index.jbuilder @@ -0,0 +1,13 @@ +json.data do + json.fiber_graph_nodes @nodes do |node| + json.(node, :alias, :node_id, :addresses, :peer_id, :timestamp, :chain_hash, :connected_node_ids, :open_channels_count) + json.timestamp node.timestamp.to_s + json.auto_accept_min_ckb_funding_amount node.auto_accept_min_ckb_funding_amount.to_s + json.total_capacity node.total_capacity.to_s + json.udt_cfg_infos node.udt_cfg_infos + end +end +json.meta do + json.total @nodes.total_count + json.page_size @page_size.to_i +end diff --git a/app/views/api/v2/fiber/graph_nodes/show.jbuilder b/app/views/api/v2/fiber/graph_nodes/show.jbuilder new file mode 100644 index 000000000..d0dadbc14 --- /dev/null +++ b/app/views/api/v2/fiber/graph_nodes/show.jbuilder @@ -0,0 +1,18 @@ +json.data do + json.(@node, :alias, :node_id, :addresses, :peer_id, :timestamp, :chain_hash, :connected_node_ids) + json.timestamp @node.timestamp.to_s + json.auto_accept_min_ckb_funding_amount @node.auto_accept_min_ckb_funding_amount.to_s + json.total_capacity @node.total_capacity.to_s + json.udt_cfg_infos @node.udt_cfg_infos + + json.fiber_graph_channels @graph_channels do |channel| + json.(channel, :channel_outpoint, :node1, :node2, :chain_hash, :open_transaction_info, :closed_transaction_info) + json.funding_tx_block_number channel.funding_tx_block_number.to_s + json.funding_tx_index channel.funding_tx_index.to_s + json.last_updated_timestamp channel.last_updated_timestamp.to_s + json.created_timestamp channel.created_timestamp.to_s + json.node1_to_node2_fee_rate channel.node1_to_node2_fee_rate.to_s + json.node2_to_node1_fee_rate channel.node2_to_node1_fee_rate.to_s + json.capacity channel.capacity.to_s + end +end diff --git a/app/views/api/v2/fiber/peers/index.jbuilder b/app/views/api/v2/fiber/peers/index.jbuilder new file mode 100644 index 000000000..2566792ca --- /dev/null +++ b/app/views/api/v2/fiber/peers/index.jbuilder @@ -0,0 +1,10 @@ +json.data do + json.fiber_peers @peers do |peer| + json.(peer, :name, :peer_id, :rpc_listening_addr, :first_channel_opened_at,:last_channel_updated_at, :channels_count) + json.total_local_balance peer.total_local_balance.to_s + end +end +json.meta do + json.total @peers.total_count + json.page_size @page_size.to_i +end diff --git a/app/views/api/v2/fiber/peers/show.jbuilder b/app/views/api/v2/fiber/peers/show.jbuilder new file mode 100644 index 000000000..2ea20791f --- /dev/null +++ b/app/views/api/v2/fiber/peers/show.jbuilder @@ -0,0 +1,9 @@ +json.data do + json.(@peer, :peer_id, :rpc_listening_addr, :first_channel_opened_at, :last_channel_updated_at) + json.fiber_channels @peer.fiber_channels do |peer_channel| + json.peer_id peer_channel.peer_id + json.channel_id peer_channel.channel_id + json.state_name peer_channel.state_name + json.state_flags peer_channel.state_flags + end +end diff --git a/app/workers/fiber_detect_worker.rb b/app/workers/fiber_detect_worker.rb new file mode 100644 index 000000000..6fef6759b --- /dev/null +++ b/app/workers/fiber_detect_worker.rb @@ -0,0 +1,49 @@ +class FiberDetectWorker + include Sidekiq::Worker + sidekiq_options queue: "fiber" + + def perform(peer_id = nil) + fiber_peers = peer_id.present? ? FiberPeer.where(peer_id:) : FiberPeer.all + fiber_peers.each { sync_with_fiber_channels(_1) } + end + + private + + def sync_with_fiber_channels(fiber_peer) + channels_attributes = build_channels_attributes(fiber_peer) + FiberChannel.upsert_all(channels_attributes, unique_by: %i[peer_id channel_id]) + end + + def build_channels_attributes(fiber_peer) + data = rpc.list_channels(fiber_peer.rpc_listening_addr, { peer_id: nil }) + data["result"]["channels"].map do |channel| + { + fiber_peer_id: fiber_peer.id, + peer_id: channel["peer_id"], + channel_id: channel["channel_id"], + state_name: channel["state"]["state_name"], + state_flags: parse_state_flags(channel["state"]["state_flags"]), + local_balance: channel["local_balance"].to_i(16), + offered_tlc_balance: channel["offered_tlc_balance"].to_i(16), + remote_balance: channel["remote_balance"].to_i(16), + received_tlc_balance: channel["received_tlc_balance"].to_i(16), + created_at: Time.at(channel["created_at"].to_i(16) / 10**6), + } + end + end + + def parse_state_flags(flags) + case flags + when Array + flags + when String + flags.split("|") + else + [] + end + end + + def rpc + @rpc ||= FiberCoordinator.instance + end +end diff --git a/app/workers/fiber_graph_detect_worker.rb b/app/workers/fiber_graph_detect_worker.rb new file mode 100644 index 000000000..b729b88fe --- /dev/null +++ b/app/workers/fiber_graph_detect_worker.rb @@ -0,0 +1,122 @@ +class FiberGraphDetectWorker + include Sidekiq::Worker + sidekiq_options queue: "fiber" + + def perform + # sync graph nodes and channels + ["nodes", "channels"].each { fetch_graph_infos(_1) } + + # check channel is closed + FiberGraphChannel.open_channels.each do |channel| + funding_cell = channel.funding_cell + if funding_cell.consumed_by + channel.update(closed_transaction_id: funding_cell.consumed_by_id) + end + end + end + + private + + def fetch_graph_infos(data_type) + return if ENV["FIBER_NODE_URL"].blank? + + cursor = nil + + loop do + break if cursor == "0x" + + next_cursor = send("fetch_#{data_type}", cursor) + break if next_cursor.nil? || next_cursor == cursor + + cursor = next_cursor + end + end + + def fetch_nodes(last_cursor) + data = rpc.graph_nodes(ENV["FIBER_NODE_URL"], { limit: "0x64", after: last_cursor }) + ApplicationRecord.transaction { data.dig("result", "nodes").each { upsert_node_with_cfg_info(_1) } } + data.dig("result", "last_cursor") + rescue StandardError => e + Rails.logger.error("Error fetching nodes: #{e.message}") + nil + end + + def fetch_channels(last_cursor) + data = rpc.graph_channels(ENV["FIBER_NODE_URL"], { limit: "0x64", after: last_cursor }) + channel_attributes = data.dig("result", "channels").map { build_channel_attributes(_1) }.compact + FiberGraphChannel.upsert_all(channel_attributes, unique_by: %i[channel_outpoint]) if channel_attributes.any? + data.dig("result", "last_cursor") + rescue StandardError => e + Rails.logger.error("Error fetching channels: #{e.message}") + nil + end + + def upsert_node_with_cfg_info(node) + node_attributes = { + alias: node["alias"], + node_id: node["node_id"], + addresses: node["addresses"], + timestamp: node["timestamp"].to_i(16), + chain_hash: node["chain_hash"], + peer_id: extract_peer_id(node["addresses"]), + auto_accept_min_ckb_funding_amount: node["auto_accept_min_ckb_funding_amount"], + } + + fiber_graph_node = FiberGraphNode.upsert(node_attributes, unique_by: %i[node_id], returning: %i[id]) + + return unless fiber_graph_node && node["udt_cfg_infos"].present? + + cfg_info_attributes = node["udt_cfg_infos"].map do |info| + udt = Udt.find_by(info["script"].symbolize_keys) + next unless udt + + { + fiber_graph_node_id: fiber_graph_node[0]["id"], + udt_id: udt.id, + auto_accept_amount: info["auto_accept_amount"].to_i(16), + } + end.compact + + FiberUdtCfgInfo.upsert_all(cfg_info_attributes, unique_by: %i[fiber_graph_node_id udt_id]) if cfg_info_attributes.any? + end + + def build_channel_attributes(channel) + if (udt_type_script = channel["udt_type_script"]).present? + udt = Udt.find_by(udt_type_script.symbolize_keys) + end + + channel_outpoint = channel["channel_outpoint"] + open_transaction = CkbTransaction.find_by(tx_hash: channel_outpoint[0..65]) + + { + channel_outpoint:, + funding_tx_block_number: channel["funding_tx_block_number"].to_i(16), + funding_tx_index: channel["funding_tx_index"].to_i(16), + node1: channel["node1"], + node2: channel["node2"], + last_updated_timestamp: channel["last_updated_timestamp"].to_i(16), + created_timestamp: channel["created_timestamp"], + node1_to_node2_fee_rate: channel["node1_to_node2_fee_rate"].to_i(16), + node2_to_node1_fee_rate: channel["node2_to_node1_fee_rate"].to_i(16), + capacity: channel["capacity"].to_i(16), + chain_hash: channel["chain_hash"], + open_transaction_id: open_transaction&.id, + udt_id: udt&.id, + } + end + + def extract_peer_id(addresses) + return nil if addresses.blank? + + parts = addresses[0].split("/") + p2p_index = parts.index("p2p") || parts.index("ipfs") + + if p2p_index && parts.length > p2p_index + 1 + parts[p2p_index + 1] + end + end + + def rpc + @rpc ||= FiberCoordinator.instance + end +end diff --git a/config/routes/v2.rb b/config/routes/v2.rb index b431ae327..6a74404f9 100644 --- a/config/routes/v2.rb +++ b/config/routes/v2.rb @@ -100,5 +100,11 @@ get :udt_accounts, on: :member end resources :rgb_live_cells, only: :index + namespace :fiber do + resources :peers, param: :peer_id, only: %i[index show create] + resources :channels, param: :channel_id, only: :show + resources :graph_nodes, param: :node_id, only: %i[index show] + resources :graph_channels, only: :index + end end end diff --git a/config/settings.mainnet.yml b/config/settings.mainnet.yml index d48ef34e4..bbc2f5c80 100644 --- a/config/settings.mainnet.yml +++ b/config/settings.mainnet.yml @@ -92,3 +92,6 @@ btc_time_code_hash: - "0x70d64497a075bd651e98ac030455ea200637ee325a12ad08aff03f1a117e5a62" single_use_lock_code_hash: "0x8290467a512e5b9a6b816469b0edabba1f4ac474e28ffdd604c2a7c76446bbaf" + +# fiber funding lock code hash +fiber_funding_code_hash: "" diff --git a/config/settings.testnet.yml b/config/settings.testnet.yml index 3a6c5d389..790e071cd 100644 --- a/config/settings.testnet.yml +++ b/config/settings.testnet.yml @@ -95,3 +95,6 @@ btc_time_code_hash: - "0x80a09eca26d77cea1f5a69471c59481be7404febf40ee90f886c36a948385b55" single_use_lock_code_hash: "0x8290467a512e5b9a6b816469b0edabba1f4ac474e28ffdd604c2a7c76446bbaf" + +# fiber funding lock code hash +fiber_funding_code_hash: "0x6c67887fe201ee0c7853f1682c0b77c0e6214044c156c7558269390a8afa6d7c" diff --git a/config/sidekiq.yml b/config/sidekiq.yml index eb0df0b76..7054f09af 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -5,6 +5,7 @@ - critical - default - bitcoin + - fiber - low production: diff --git a/db/migrate/20240918024407_create_fiber_peers.rb b/db/migrate/20240918024407_create_fiber_peers.rb new file mode 100644 index 000000000..bb5aa2f31 --- /dev/null +++ b/db/migrate/20240918024407_create_fiber_peers.rb @@ -0,0 +1,13 @@ +class CreateFiberPeers < ActiveRecord::Migration[7.0] + def change + create_table :fiber_peers do |t| + t.string :name + t.string :peer_id + t.string :rpc_listening_addr + t.datetime :first_channel_opened_at + t.datetime :last_channel_updated_at + + t.timestamps + end + end +end diff --git a/db/migrate/20240918024415_create_fiber_channels.rb b/db/migrate/20240918024415_create_fiber_channels.rb new file mode 100644 index 000000000..7b536f5cf --- /dev/null +++ b/db/migrate/20240918024415_create_fiber_channels.rb @@ -0,0 +1,19 @@ +class CreateFiberChannels < ActiveRecord::Migration[7.0] + def change + create_table :fiber_channels do |t| + t.string :peer_id + t.string :channel_id + t.string :state_name + t.string :state_flags, default: [], array: true + t.decimal :local_balance, precision: 64, scale: 2, default: 0.0 + t.decimal :sent_tlc_balance, precision: 64, scale: 2, default: 0.0 + t.decimal :remote_balance, precision: 64, scale: 2, default: 0.0 + t.decimal :received_tlc_balance, precision: 64, scale: 2, default: 0.0 + t.datetime :shutdown_at + + t.timestamps + end + + add_index :fiber_channels, %i[peer_id channel_id], unique: true + end +end diff --git a/db/migrate/20240918033146_create_fiber_transactions.rb b/db/migrate/20240918033146_create_fiber_transactions.rb new file mode 100644 index 000000000..22f36d455 --- /dev/null +++ b/db/migrate/20240918033146_create_fiber_transactions.rb @@ -0,0 +1,11 @@ +class CreateFiberTransactions < ActiveRecord::Migration[7.0] + def change + create_table :fiber_transactions do |t| + t.integer :fiber_peer_id + t.integer :fiber_channel_id + t.integer :ckb_transaction_id + + t.timestamps + end + end +end diff --git a/db/migrate/20240920094807_add_fiber_peer_id_to_fiber_channels.rb b/db/migrate/20240920094807_add_fiber_peer_id_to_fiber_channels.rb new file mode 100644 index 000000000..375d2683d --- /dev/null +++ b/db/migrate/20240920094807_add_fiber_peer_id_to_fiber_channels.rb @@ -0,0 +1,7 @@ +class AddFiberPeerIdToFiberChannels < ActiveRecord::Migration[7.0] + def change + rename_column :fiber_channels, :sent_tlc_balance, :offered_tlc_balance + add_column :fiber_channels, :fiber_peer_id, :integer + add_index :fiber_channels, :fiber_peer_id + end +end diff --git a/db/migrate/20240924065539_update_rpc_listening_addr_to_array.rb b/db/migrate/20240924065539_update_rpc_listening_addr_to_array.rb new file mode 100644 index 000000000..d70a90ca4 --- /dev/null +++ b/db/migrate/20240924065539_update_rpc_listening_addr_to_array.rb @@ -0,0 +1,5 @@ +class UpdateRpcListeningAddrToArray < ActiveRecord::Migration[7.0] + def change + change_column :fiber_peers, :rpc_listening_addr, :string, array: true, default: [], using: "(string_to_array(rpc_listening_addr, ','))" + end +end diff --git a/db/migrate/20241012014906_create_fiber_graph_infos.rb b/db/migrate/20241012014906_create_fiber_graph_infos.rb new file mode 100644 index 000000000..39637c7d0 --- /dev/null +++ b/db/migrate/20241012014906_create_fiber_graph_infos.rb @@ -0,0 +1,33 @@ +class CreateFiberGraphInfos < ActiveRecord::Migration[7.0] + def change + create_table :fiber_graph_nodes do |t| + t.string :alias + t.string :node_id + t.string :addresses, array: true, default: [], using: "(string_to_array(addresses, ','))" + t.bigint :timestamp + t.string :chain_hash + t.decimal :auto_accept_min_ckb_funding_amount, precision: 30 + + t.timestamps + end + + create_table :fiber_graph_channels do |t| + t.string :channel_outpoint + t.bigint :funding_tx_block_number + t.integer :funding_tx_index + t.string :node1 + t.string :node2 + t.bigint :last_updated_timestamp + t.bigint :created_timestamp + t.decimal :node1_to_node2_fee_rate, precision: 30, default: 0.0 + t.decimal :node2_to_node1_fee_rate, precision: 30, default: 0.0 + t.decimal :capacity, precision: 64, scale: 2, default: 0.0 + t.string :chain_hash + + t.timestamps + end + + add_index :fiber_graph_nodes, :node_id, unique: true + add_index :fiber_graph_channels, :channel_outpoint, unique: true + end +end diff --git a/db/migrate/20241023055256_create_fiber_udt_cfg_infos.rb b/db/migrate/20241023055256_create_fiber_udt_cfg_infos.rb new file mode 100644 index 000000000..54faa718a --- /dev/null +++ b/db/migrate/20241023055256_create_fiber_udt_cfg_infos.rb @@ -0,0 +1,13 @@ +class CreateFiberUdtCfgInfos < ActiveRecord::Migration[7.0] + def change + create_table :fiber_udt_cfg_infos do |t| + t.bigint :fiber_graph_node_id + t.bigint :udt_id + t.decimal :auto_accept_amount, precision: 64, scale: 2, default: 0.0 + + t.timestamps + end + + add_index :fiber_udt_cfg_infos, %i[fiber_graph_node_id udt_id], unique: true + end +end diff --git a/db/migrate/20241023063536_add_udt_id_to_fiber_graph_channels.rb b/db/migrate/20241023063536_add_udt_id_to_fiber_graph_channels.rb new file mode 100644 index 000000000..20c001b16 --- /dev/null +++ b/db/migrate/20241023063536_add_udt_id_to_fiber_graph_channels.rb @@ -0,0 +1,5 @@ +class AddUdtIdToFiberGraphChannels < ActiveRecord::Migration[7.0] + def change + add_column :fiber_graph_channels, :udt_id, :bigint + end +end diff --git a/db/migrate/20241030023309_add_transaction_id_to_fiber_graph_channels.rb b/db/migrate/20241030023309_add_transaction_id_to_fiber_graph_channels.rb new file mode 100644 index 000000000..212379ae3 --- /dev/null +++ b/db/migrate/20241030023309_add_transaction_id_to_fiber_graph_channels.rb @@ -0,0 +1,8 @@ +class AddTransactionIdToFiberGraphChannels < ActiveRecord::Migration[7.0] + def change + change_table :fiber_graph_channels, bulk: true do |t| + t.bigint :open_transaction_id + t.bigint :closed_transaction_id + end + end +end diff --git a/db/migrate/20241129032447_add_peer_id_to_fiber_graph_nodes.rb b/db/migrate/20241129032447_add_peer_id_to_fiber_graph_nodes.rb new file mode 100644 index 000000000..f25285b0e --- /dev/null +++ b/db/migrate/20241129032447_add_peer_id_to_fiber_graph_nodes.rb @@ -0,0 +1,5 @@ +class AddPeerIdToFiberGraphNodes < ActiveRecord::Migration[7.0] + def change + add_column :fiber_graph_nodes, :peer_id, :string + end +end diff --git a/db/structure.sql b/db/structure.sql index bbe7b508d..b1b2bedbb 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23,20 +23,6 @@ CREATE EXTENSION IF NOT EXISTS btree_gin WITH SCHEMA public; COMMENT ON EXTENSION btree_gin IS 'support for indexing common datatypes in GIN'; --- --- Name: pg_buffercache; Type: EXTENSION; Schema: -; Owner: - --- - -CREATE EXTENSION IF NOT EXISTS pg_buffercache WITH SCHEMA public; - - --- --- Name: EXTENSION pg_buffercache; Type: COMMENT; Schema: -; Owner: - --- - -COMMENT ON EXTENSION pg_buffercache IS 'examine the shared buffer cache'; - - -- -- Name: array_subtract(anyarray, anyarray); Type: FUNCTION; Schema: public; Owner: - -- @@ -188,7 +174,7 @@ begin insert into account_books (ckb_transaction_id, address_id) values (row.id, i) ON CONFLICT DO NOTHING; end loop; - END LOOP; + END LOOP; close c; end $$; @@ -210,21 +196,21 @@ DECLARE if new.contained_address_ids is null then new.contained_address_ids := array[]::int[]; end if; - if old is null + if old is null then to_add := new.contained_address_ids; to_remove := array[]::int[]; else - + to_add := array_subtract(new.contained_address_ids, old.contained_address_ids); - to_remove := array_subtract(old.contained_address_ids, new.contained_address_ids); + to_remove := array_subtract(old.contained_address_ids, new.contained_address_ids); end if; if to_add is not null then FOREACH i IN ARRAY to_add - LOOP + LOOP RAISE NOTICE 'ckb_tx_addr_id(%)', i; - insert into account_books (ckb_transaction_id, address_id) + insert into account_books (ckb_transaction_id, address_id) values (new.id, i); END LOOP; end if; @@ -429,9 +415,9 @@ CREATE TABLE public.addresses ( dao_deposit numeric(30,0) DEFAULT 0.0, interest numeric(30,0) DEFAULT 0.0, block_timestamp bigint, - visible boolean DEFAULT true, live_cells_count bigint DEFAULT 0.0, mined_blocks_count integer DEFAULT 0, + visible boolean DEFAULT true, average_deposit_time bigint, unclaimed_compensation numeric(30,0), is_depositor boolean DEFAULT false, @@ -484,6 +470,7 @@ CREATE TABLE public.blocks ( "timestamp" bigint, transactions_root bytea, proposals_hash bytea, + uncles_count integer, extra_hash bytea, uncle_block_hashes bytea, version integer, @@ -510,7 +497,6 @@ CREATE TABLE public.blocks ( nonce numeric(50,0) DEFAULT 0.0, start_number numeric(30,0) DEFAULT 0.0, length numeric(30,0) DEFAULT 0.0, - uncles_count integer, compact_target numeric(20,0), live_cell_changes integer, block_time bigint, @@ -520,8 +506,8 @@ CREATE TABLE public.blocks ( miner_message character varying, extension jsonb, median_timestamp bigint DEFAULT 0.0, - cycles bigint, - ckb_node_version character varying + ckb_node_version character varying, + cycles bigint ); @@ -672,6 +658,39 @@ CREATE SEQUENCE public.bitcoin_statistics_id_seq ALTER SEQUENCE public.bitcoin_statistics_id_seq OWNED BY public.bitcoin_statistics.id; +-- +-- Name: bitcoin_time_locks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.bitcoin_time_locks ( + id bigint NOT NULL, + bitcoin_transaction_id bigint, + ckb_transaction_id bigint, + cell_output_id bigint, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: bitcoin_time_locks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.bitcoin_time_locks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: bitcoin_time_locks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.bitcoin_time_locks_id_seq OWNED BY public.bitcoin_time_locks.id; + + -- -- Name: bitcoin_transactions; Type: TABLE; Schema: public; Owner: - -- @@ -1516,9 +1535,9 @@ ALTER SEQUENCE public.contracts_id_seq OWNED BY public.contracts.id; CREATE TABLE public.daily_statistics ( id bigint NOT NULL, - transactions_count character varying DEFAULT '0'::character varying, - addresses_count character varying DEFAULT '0'::character varying, - total_dao_deposit character varying DEFAULT '0.0'::character varying, + transactions_count character varying DEFAULT 0, + addresses_count character varying DEFAULT 0, + total_dao_deposit character varying DEFAULT 0.0, block_timestamp numeric(30,0), created_at_unixtimestamp integer, created_at timestamp(6) without time zone NOT NULL, @@ -1537,8 +1556,8 @@ CREATE TABLE public.daily_statistics ( avg_difficulty character varying DEFAULT '0'::character varying, uncle_rate character varying DEFAULT '0'::character varying, total_depositors_count character varying DEFAULT '0'::character varying, - address_balance_distribution jsonb, total_tx_fee numeric(30,0), + address_balance_distribution jsonb, occupied_capacity numeric(30,0), daily_dao_deposit numeric(30,0), daily_dao_depositors_count integer, @@ -1728,6 +1747,230 @@ CREATE SEQUENCE public.epoch_statistics_id_seq ALTER SEQUENCE public.epoch_statistics_id_seq OWNED BY public.epoch_statistics.id; +-- +-- Name: fiber_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_channels ( + id bigint NOT NULL, + peer_id character varying, + channel_id character varying, + state_name character varying, + state_flags character varying[] DEFAULT '{}'::character varying[], + local_balance numeric(64,2) DEFAULT 0.0, + offered_tlc_balance numeric(64,2) DEFAULT 0.0, + remote_balance numeric(64,2) DEFAULT 0.0, + received_tlc_balance numeric(64,2) DEFAULT 0.0, + shutdown_at timestamp(6) without time zone, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + fiber_peer_id integer +); + + +-- +-- Name: fiber_channels_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_channels_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_channels_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_channels_id_seq OWNED BY public.fiber_channels.id; + + +-- +-- Name: fiber_graph_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_graph_channels ( + id bigint NOT NULL, + channel_outpoint character varying, + funding_tx_block_number bigint, + funding_tx_index integer, + node1 character varying, + node2 character varying, + last_updated_timestamp bigint, + created_timestamp bigint, + node1_to_node2_fee_rate numeric(30,0) DEFAULT 0.0, + node2_to_node1_fee_rate numeric(30,0) DEFAULT 0.0, + capacity numeric(64,2) DEFAULT 0.0, + chain_hash character varying, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + udt_id bigint, + open_transaction_id bigint, + closed_transaction_id bigint +); + + +-- +-- Name: fiber_graph_channels_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_graph_channels_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_graph_channels_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_graph_channels_id_seq OWNED BY public.fiber_graph_channels.id; + + +-- +-- Name: fiber_graph_nodes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_graph_nodes ( + id bigint NOT NULL, + alias character varying, + node_id character varying, + addresses character varying[] DEFAULT '{}'::character varying[], + "timestamp" bigint, + chain_hash character varying, + auto_accept_min_ckb_funding_amount numeric(30,0), + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + peer_id character varying +); + + +-- +-- Name: fiber_graph_nodes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_graph_nodes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_graph_nodes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_graph_nodes_id_seq OWNED BY public.fiber_graph_nodes.id; + + +-- +-- Name: fiber_peers; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_peers ( + id bigint NOT NULL, + name character varying, + peer_id character varying, + rpc_listening_addr character varying[] DEFAULT '{}'::character varying[], + first_channel_opened_at timestamp(6) without time zone, + last_channel_updated_at timestamp(6) without time zone, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL, + node_id character varying, + chain_hash character varying +); + + +-- +-- Name: fiber_peers_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_peers_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_peers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_peers_id_seq OWNED BY public.fiber_peers.id; + + +-- +-- Name: fiber_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_transactions ( + id bigint NOT NULL, + fiber_peer_id integer, + fiber_channel_id integer, + ckb_transaction_id integer, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: fiber_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_transactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_transactions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_transactions_id_seq OWNED BY public.fiber_transactions.id; + + +-- +-- Name: fiber_udt_cfg_infos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.fiber_udt_cfg_infos ( + id bigint NOT NULL, + fiber_graph_node_id bigint, + udt_id bigint, + auto_accept_amount numeric(64,2) DEFAULT 0.0, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: fiber_udt_cfg_infos_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.fiber_udt_cfg_infos_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: fiber_udt_cfg_infos_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.fiber_udt_cfg_infos_id_seq OWNED BY public.fiber_udt_cfg_infos.id; + + -- -- Name: forked_blocks; Type: TABLE; Schema: public; Owner: - -- @@ -1740,6 +1983,7 @@ CREATE TABLE public.forked_blocks ( "timestamp" bigint, transactions_root bytea, proposals_hash bytea, + uncles_count integer, extra_hash bytea, uncle_block_hashes bytea, version integer, @@ -1766,7 +2010,6 @@ CREATE TABLE public.forked_blocks ( nonce numeric(50,0) DEFAULT 0.0, start_number numeric(30,0) DEFAULT 0.0, length numeric(30,0) DEFAULT 0.0, - uncles_count integer, compact_target numeric(20,0), live_cell_changes integer, block_time numeric(13,0), @@ -1776,8 +2019,8 @@ CREATE TABLE public.forked_blocks ( miner_message character varying, extension jsonb, median_timestamp numeric DEFAULT 0.0, - cycles bigint, - ckb_node_version character varying + ckb_node_version character varying, + cycles bigint ); @@ -2961,6 +3204,13 @@ ALTER TABLE ONLY public.bitcoin_annotations ALTER COLUMN id SET DEFAULT nextval( ALTER TABLE ONLY public.bitcoin_statistics ALTER COLUMN id SET DEFAULT nextval('public.bitcoin_statistics_id_seq'::regclass); +-- +-- Name: bitcoin_time_locks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.bitcoin_time_locks ALTER COLUMN id SET DEFAULT nextval('public.bitcoin_time_locks_id_seq'::regclass); + + -- -- Name: bitcoin_transactions id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3108,6 +3358,48 @@ ALTER TABLE ONLY public.deployed_cells ALTER COLUMN id SET DEFAULT nextval('publ ALTER TABLE ONLY public.epoch_statistics ALTER COLUMN id SET DEFAULT nextval('public.epoch_statistics_id_seq'::regclass); +-- +-- Name: fiber_channels id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_channels ALTER COLUMN id SET DEFAULT nextval('public.fiber_channels_id_seq'::regclass); + + +-- +-- Name: fiber_graph_channels id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_graph_channels ALTER COLUMN id SET DEFAULT nextval('public.fiber_graph_channels_id_seq'::regclass); + + +-- +-- Name: fiber_graph_nodes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_graph_nodes ALTER COLUMN id SET DEFAULT nextval('public.fiber_graph_nodes_id_seq'::regclass); + + +-- +-- Name: fiber_peers id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_peers ALTER COLUMN id SET DEFAULT nextval('public.fiber_peers_id_seq'::regclass); + + +-- +-- Name: fiber_transactions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_transactions ALTER COLUMN id SET DEFAULT nextval('public.fiber_transactions_id_seq'::regclass); + + +-- +-- Name: fiber_udt_cfg_infos id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_udt_cfg_infos ALTER COLUMN id SET DEFAULT nextval('public.fiber_udt_cfg_infos_id_seq'::regclass); + + -- -- Name: forked_blocks id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3375,6 +3667,14 @@ ALTER TABLE ONLY public.bitcoin_statistics ADD CONSTRAINT bitcoin_statistics_pkey PRIMARY KEY (id); +-- +-- Name: bitcoin_time_locks bitcoin_time_locks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.bitcoin_time_locks + ADD CONSTRAINT bitcoin_time_locks_pkey PRIMARY KEY (id); + + -- -- Name: bitcoin_transactions bitcoin_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3647,6 +3947,54 @@ ALTER TABLE ONLY public.epoch_statistics ADD CONSTRAINT epoch_statistics_pkey PRIMARY KEY (id); +-- +-- Name: fiber_channels fiber_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_channels + ADD CONSTRAINT fiber_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: fiber_graph_channels fiber_graph_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_graph_channels + ADD CONSTRAINT fiber_graph_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: fiber_graph_nodes fiber_graph_nodes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_graph_nodes + ADD CONSTRAINT fiber_graph_nodes_pkey PRIMARY KEY (id); + + +-- +-- Name: fiber_peers fiber_peers_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_peers + ADD CONSTRAINT fiber_peers_pkey PRIMARY KEY (id); + + +-- +-- Name: fiber_transactions fiber_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_transactions + ADD CONSTRAINT fiber_transactions_pkey PRIMARY KEY (id); + + +-- +-- Name: fiber_udt_cfg_infos fiber_udt_cfg_infos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.fiber_udt_cfg_infos + ADD CONSTRAINT fiber_udt_cfg_infos_pkey PRIMARY KEY (id); + + -- -- Name: forked_blocks forked_blocks_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4513,6 +4861,13 @@ CREATE UNIQUE INDEX index_bitcoin_annotations_on_ckb_transaction_id ON public.bi CREATE UNIQUE INDEX index_bitcoin_statistics_on_timestamp ON public.bitcoin_statistics USING btree ("timestamp"); +-- +-- Name: index_bitcoin_time_locks_on_cell; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_bitcoin_time_locks_on_cell ON public.bitcoin_time_locks USING btree (bitcoin_transaction_id, cell_output_id); + + -- -- Name: index_bitcoin_transactions_on_txid; Type: INDEX; Schema: public; Owner: - -- @@ -4800,6 +5155,48 @@ CREATE UNIQUE INDEX index_deployed_cells_on_contract_id_and_cell_output_id ON pu CREATE UNIQUE INDEX index_epoch_statistics_on_epoch_number ON public.epoch_statistics USING btree (epoch_number); +-- +-- Name: index_fiber_channels_on_fiber_peer_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_fiber_channels_on_fiber_peer_id ON public.fiber_channels USING btree (fiber_peer_id); + + +-- +-- Name: index_fiber_channels_on_peer_id_and_channel_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_fiber_channels_on_peer_id_and_channel_id ON public.fiber_channels USING btree (peer_id, channel_id); + + +-- +-- Name: index_fiber_graph_channels_on_channel_outpoint; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_fiber_graph_channels_on_channel_outpoint ON public.fiber_graph_channels USING btree (channel_outpoint); + + +-- +-- Name: index_fiber_graph_nodes_on_node_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_fiber_graph_nodes_on_node_id ON public.fiber_graph_nodes USING btree (node_id); + + +-- +-- Name: index_fiber_peers_on_peer_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_fiber_peers_on_peer_id ON public.fiber_peers USING btree (peer_id); + + +-- +-- Name: index_fiber_udt_cfg_infos_on_fiber_graph_node_id_and_udt_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_fiber_udt_cfg_infos_on_fiber_graph_node_id_and_udt_id ON public.fiber_udt_cfg_infos USING btree (fiber_graph_node_id, udt_id); + + -- -- Name: index_forked_events_on_status; Type: INDEX; Schema: public; Owner: - -- @@ -4905,6 +5302,13 @@ CREATE INDEX index_on_cell_dependencies_contract_cell_block_tx ON public.cell_de CREATE UNIQUE INDEX index_portfolios_on_user_id_and_address_id ON public.portfolios USING btree (user_id, address_id); +-- +-- Name: index_referring_cells_on_cell_output_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_referring_cells_on_cell_output_id ON public.referring_cells USING btree (cell_output_id); + + -- -- Name: index_referring_cells_on_contract_id_and_cell_output_id; Type: INDEX; Schema: public; Owner: - -- @@ -6018,6 +6422,15 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240823071420'), ('20240902025657'), ('20240904043807'), +('20240918024407'), +('20240918024415'), +('20240918033146'), +('20240920094807'), +('20240924065539'), +('20241012014906'), +('20241023055256'), +('20241023063536'), +('20241030023309'), ('20241105070340'), ('20241105070619'), ('20241106062022'), @@ -6026,6 +6439,5 @@ INSERT INTO "schema_migrations" (version) VALUES ('20241121073245'), ('20241125100650'), ('20241129000339'), +('20241129032447'), ('20241202072604'); - - diff --git a/lib/scheduler.rb b/lib/scheduler.rb index 364ab7997..9afe16d6f 100644 --- a/lib/scheduler.rb +++ b/lib/scheduler.rb @@ -132,4 +132,8 @@ def call_worker(clz) call_worker AnalyzeContractFromCellDependencyWorker end +s.every "5m", overlap: false do + call_worker FiberGraphDetectWorker +end + s.join diff --git a/test/factories/fiber_channels.rb b/test/factories/fiber_channels.rb new file mode 100644 index 000000000..7a55b0264 --- /dev/null +++ b/test/factories/fiber_channels.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :fiber_channel do + + end +end diff --git a/test/factories/fiber_graph_channels.rb b/test/factories/fiber_graph_channels.rb new file mode 100644 index 000000000..f63ba3d55 --- /dev/null +++ b/test/factories/fiber_graph_channels.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :fiber_graph_channel do + + end +end diff --git a/test/factories/fiber_graph_nodes.rb b/test/factories/fiber_graph_nodes.rb new file mode 100644 index 000000000..a387eeb93 --- /dev/null +++ b/test/factories/fiber_graph_nodes.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :fiber_graph_node do + + end +end diff --git a/test/factories/fiber_peers.rb b/test/factories/fiber_peers.rb new file mode 100644 index 000000000..96b8b3aa3 --- /dev/null +++ b/test/factories/fiber_peers.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :fiber_peer do + + end +end diff --git a/test/factories/fiber_udt_cfg_infos.rb b/test/factories/fiber_udt_cfg_infos.rb new file mode 100644 index 000000000..f9cbd8b2a --- /dev/null +++ b/test/factories/fiber_udt_cfg_infos.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :fiber_udt_cfg_info do + + end +end diff --git a/test/models/fiber_channel_test.rb b/test/models/fiber_channel_test.rb new file mode 100644 index 000000000..25e751ac1 --- /dev/null +++ b/test/models/fiber_channel_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class FiberChannelTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/fiber_graph_channel_test.rb b/test/models/fiber_graph_channel_test.rb new file mode 100644 index 000000000..d9674a776 --- /dev/null +++ b/test/models/fiber_graph_channel_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class FiberGraphChannelTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/fiber_graph_node_test.rb b/test/models/fiber_graph_node_test.rb new file mode 100644 index 000000000..ef430b005 --- /dev/null +++ b/test/models/fiber_graph_node_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class FiberGraphNodeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/fiber_peer_test.rb b/test/models/fiber_peer_test.rb new file mode 100644 index 000000000..f179352d2 --- /dev/null +++ b/test/models/fiber_peer_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class FiberPeerTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/fiber_udt_cfg_info_test.rb b/test/models/fiber_udt_cfg_info_test.rb new file mode 100644 index 000000000..80fdf45d9 --- /dev/null +++ b/test/models/fiber_udt_cfg_info_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class FiberUdtCfgInfoTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end