Skip to content

Commit f6cda10

Browse files
authored
Merge pull request #2571 from nervosnetwork/testnet
Deploy to mainnet
2 parents ff4e18b + d5a2c13 commit f6cda10

18 files changed

+454
-67
lines changed

app/controllers/api/v2/fiber/graph_nodes_controller.rb

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,38 @@ module Api
22
module V2
33
module Fiber
44
class GraphNodesController < BaseController
5+
before_action :find_node, only: %i[show graph_channels transactions]
6+
57
def index
6-
@page = params.fetch(:page, 1)
7-
@page_size = params.fetch(:page_size, FiberGraphNode.default_per_page)
8-
@nodes =
9-
if params[:q].present?
10-
FiberGraphNode.with_deleted.where("node_name = :q or peer_id = :q or node_id = :q", q: params[:q]).page(@page).per(@page_size)
11-
else
12-
FiberGraphNode.with_deleted.page(@page).per(@page_size)
13-
end
8+
@nodes = GraphNodes::Index.run!({ key: params[:q], page: params[:page], page_size: params[:page_size] })
149
end
1510

16-
def show
17-
@node = FiberGraphNode.with_deleted.find_by(node_id: params[:node_id])
18-
raise Api::V2::Exceptions::FiberGraphNodeNotFoundError unless @node
11+
def show; end
1912

20-
@graph_channels = FiberGraphChannel.with_deleted.where(node1: params[:node_id]).or(
21-
FiberGraphChannel.with_deleted.where(node2: params[:node_id]),
22-
)
13+
def graph_channels
14+
@channels = GraphNodes::GraphChannels.run!(query_params)
15+
end
2316

24-
if params[:status] == "closed"
25-
@graph_channels = @graph_channels.with_deleted.where.not(closed_transaction_id: nil)
26-
end
17+
def transactions
18+
@transactions = GraphNodes::Transactions.run!(query_params)
2719
end
2820

2921
def addresses
3022
nodes = FiberGraphNode.all.select(:node_id, :addresses)
3123
render json: { data: nodes.map { { node_id: _1.node_id, addresses: _1.addresses, connections: _1.connected_node_ids } } }
3224
end
25+
26+
private
27+
28+
def find_node
29+
@node = FiberGraphNode.with_deleted.find_by(node_id: params[:node_id])
30+
raise Api::V2::Exceptions::FiberGraphNodeNotFoundError unless @node
31+
end
32+
33+
def query_params
34+
params.permit(:node_id, :sort, :page, :page_size, :type_hash, :min_token_amount, :max_token_amount,
35+
:address_hash, :status, :start_date, :end_date)
36+
end
3337
end
3438
end
3539
end

app/interactions/addresses/explore.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ def execute
1212
def find_address
1313
return Address.find_by(lock_hash: key) if QueryKeyUtils.valid_hex?(key)
1414
return find_by_address_hash(key) if QueryKeyUtils.valid_address?(key)
15-
return find_by_bitcoin_address_hash(key) if BitcoinUtils.valid_address?(key)
15+
16+
find_by_bitcoin_address_hash(key) if BitcoinUtils.valid_address?(key)
1617
end
1718

1819
def find_by_address_hash(key)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
module GraphNodes
2+
class GraphChannels < ActiveInteraction::Base
3+
string :node_id, default: nil
4+
string :sort, default: "position_time.desc"
5+
integer :page, default: 1
6+
integer :page_size, default: FiberGraphChannel.default_per_page
7+
8+
string :type_hash, default: nil
9+
decimal :min_token_amount, default: nil
10+
decimal :max_token_amount, default: nil
11+
12+
string :address_hash, default: nil
13+
string :status, default: nil
14+
15+
integer :start_date, default: nil
16+
integer :end_date, default: nil
17+
18+
validates :status, inclusion: { in: %w[open closed], allow_nil: true }
19+
validate :validate_date!
20+
21+
def execute
22+
scope = FiberGraphChannel.with_deleted
23+
scope = scope.where(node1: node_id).or(scope.where(node2: node_id))
24+
return FiberGraphChannel.none if scope.empty?
25+
26+
scope = filter_by_address(scope)
27+
scope = filter_by_status(scope)
28+
scope = filter_by_date(scope)
29+
scope = filter_by_type_hash(scope)
30+
31+
sort_column, sort_direction = channels_ordering
32+
scope.order(sort_column => sort_direction).page(page).per(page_size).fast_page
33+
end
34+
35+
private
36+
37+
def filter_by_address(scope)
38+
return scope unless address_hash
39+
40+
address = Address.find_address!(address_hash)
41+
scope.includes(:fiber_account_books).where(fiber_account_books: { address: })
42+
end
43+
44+
def filter_by_status(scope)
45+
case status
46+
when "closed"
47+
scope.where.not(closed_transaction_id: nil)
48+
when "open"
49+
scope.where(closed_transaction_id: nil)
50+
else
51+
scope
52+
end
53+
end
54+
55+
def filter_by_date(scope)
56+
scope = scope.where(created_timestamp: start_date..) if start_date
57+
scope = scope.where(created_timestamp: ..end_date) if end_date
58+
scope
59+
end
60+
61+
def filter_by_type_hash(scope)
62+
return scope unless type_hash
63+
64+
if type_hash.eql?("0x0")
65+
scope = scope.where(udt_id: nil)
66+
scope = scope.where(capacity: min_token_amount..) if min_token_amount
67+
scope = scope.where(capacity: ..max_token_amount) if max_token_amount
68+
else
69+
scope = scope.includes(:udt).where(udt: { type_hash: })
70+
scope = scope.includes(:funding_cell).where(funding_cell: { udt_amount: min_token_amount.. }) if min_token_amount
71+
scope = scope.includes(:funding_cell).where(funding_cell: { udt_amount: ..max_token_amount }) if max_token_amount
72+
end
73+
74+
scope
75+
end
76+
77+
def channels_ordering
78+
sort_by_param, sort_order = sort.to_s.split(".", 2)
79+
sort_by = { "position_time" => "created_timestamp" }.fetch(sort_by_param, "capacity")
80+
sort_order = sort_order&.downcase
81+
sort_order = "asc" unless %w[asc desc].include?(sort_order)
82+
83+
[sort_by, sort_order]
84+
end
85+
86+
def validate_date!
87+
if start_date.present? && end_date.present? && start_date > end_date
88+
raise "invalid date"
89+
end
90+
end
91+
end
92+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module GraphNodes
2+
class Index < ActiveInteraction::Base
3+
string :key, default: nil
4+
integer :page, default: 1
5+
integer :page_size, default: FiberGraphNode.default_per_page
6+
7+
def execute
8+
scope = FiberGraphNode.with_deleted
9+
scope = scope.where("node_name = :key or peer_id = :key or node_id = :key", key:) if key
10+
scope.page(page).per(page_size).fast_page
11+
end
12+
end
13+
end
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
module GraphNodes
2+
class Transactions < ActiveInteraction::Base
3+
string :node_id, default: nil
4+
string :sort, default: "block_timestamp.desc"
5+
integer :page, default: 1
6+
integer :page_size, default: FiberGraphChannel.default_per_page
7+
8+
string :type_hash, default: nil
9+
decimal :min_token_amount, default: nil
10+
decimal :max_token_amount, default: nil
11+
12+
string :address_hash, default: nil
13+
string :status, default: nil
14+
15+
integer :start_date, default: nil
16+
integer :end_date, default: nil
17+
18+
validates :status, inclusion: { in: %w[open closed], allow_nil: true }
19+
20+
def execute
21+
channels = FiberGraphChannel.with_deleted
22+
channels = channels.where(node1: node_id).or(channels.where(node2: node_id))
23+
return FiberGraphChannel.none if channels.empty?
24+
25+
channels = filter_by_address(channels)
26+
channels = filter_by_type_hash(channels)
27+
28+
wrap_result(channels)
29+
end
30+
31+
private
32+
33+
def filter_by_address(channels)
34+
return channels unless address_hash
35+
36+
address = Address.find_address!(address_hash)
37+
channels.includes(:fiber_account_books).where(fiber_account_books: { address: })
38+
end
39+
40+
def filter_by_type_hash(channels)
41+
return channels unless type_hash
42+
43+
if type_hash.eql?("0x0")
44+
channels = channels.where(udt_id: nil)
45+
channels = channels.where(capacity: min_token_amount..) if min_token_amount
46+
channels = channels.where(capacity: ..max_token_amount) if max_token_amount
47+
else
48+
channels = channels.includes(:udt).where(udt: { type_hash: })
49+
channels = channels.includes(:funding_cell).where(funding_cell: { udt_amount: min_token_amount.. }) if min_token_amount
50+
channels = channels.includes(:funding_cell).where(funding_cell: { udt_amount: ..max_token_amount }) if max_token_amount
51+
end
52+
53+
channels
54+
end
55+
56+
def wrap_result(channels)
57+
transactions = channels.flat_map do |channel|
58+
list = []
59+
list << transaction_data(channel.open_transaction_info, channel, true)
60+
if channel.closed_transaction
61+
list << transaction_data(channel.closed_transaction_info, channel, false)
62+
end
63+
list
64+
end
65+
66+
transactions = sort_transactions(transactions)
67+
transactions = filter_by_status(transactions)
68+
transactions = filter_by_block_timestamp(transactions)
69+
70+
Kaminari.paginate_array(transactions).page(page).per(page_size)
71+
end
72+
73+
def transaction_data(tx_info, channel, is_open)
74+
{ is_open: is_open, is_udt: channel.udt.present? }.merge(tx_info)
75+
end
76+
77+
def sort_transactions(transactions)
78+
direction = sort == "block_timestamp.desc" ? -1 : 1
79+
transactions.sort_by { |tx| tx[:block_timestamp] * direction }
80+
end
81+
82+
def filter_by_block_timestamp(transactions)
83+
return transactions unless start_date || end_date
84+
85+
transactions.select do |tx|
86+
ts = tx[:block_timestamp].to_i
87+
(start_date.nil? || ts >= start_date) && (end_date.nil? || ts <= end_date)
88+
end
89+
end
90+
91+
def filter_by_status(transactions)
92+
case status
93+
when "open"
94+
transactions.select { |t| t[:is_open] }
95+
when "closed"
96+
transactions.reject { |t| t[:is_open] }
97+
else
98+
transactions
99+
end
100+
end
101+
end
102+
end

app/models/fiber_account_book.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class FiberAccountBook < ApplicationRecord
2+
belongs_to :fiber_graph_channel, -> { with_deleted }
3+
belongs_to :address
4+
belongs_to :ckb_transaction
5+
end
6+
7+
# == Schema Information
8+
#
9+
# Table name: fiber_account_books
10+
#
11+
# id :bigint not null, primary key
12+
# fiber_graph_channel_id :bigint
13+
# ckb_transaction_id :bigint
14+
# address_id :bigint
15+
#
16+
# Indexes
17+
#
18+
# index_fiber_account_books_on_address_id_and_ckb_transaction_id (address_id,ckb_transaction_id) UNIQUE
19+
# index_fiber_account_books_on_ckb_transaction_id (ckb_transaction_id)
20+
#

app/models/fiber_graph_channel.rb

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,36 @@ class FiberGraphChannel < ApplicationRecord
66
paginates_per DEFAULT_PAGINATES_PER
77
max_paginates_per MAX_PAGINATES_PER
88

9+
has_many :fiber_account_books
910
belongs_to :udt, optional: true
1011
belongs_to :open_transaction, class_name: "CkbTransaction"
1112
belongs_to :closed_transaction, class_name: "CkbTransaction", optional: true
13+
belongs_to :funding_cell, class_name: "CellOutput", foreign_key: :cell_output_id
1214
belongs_to :address
13-
belongs_to :cell_output
1415

1516
validates :open_transaction_id, presence: true
1617

1718
scope :open_channels, -> { where(closed_transaction_id: nil) }
1819

1920
def open_transaction_info
20-
open_transaction.as_json(only: %i[tx_hash block_number block_timestamp]).merge(
21-
{
22-
capacity: funding_cell.capacity,
23-
udt_info: funding_cell.udt_info,
24-
address: funding_cell.address_hash,
25-
},
26-
)
21+
{
22+
tx_hash: open_transaction.tx_hash,
23+
block_number: open_transaction.block_number,
24+
block_timestamp: open_transaction.block_timestamp,
25+
capacity: funding_cell.capacity,
26+
udt_info: funding_cell.udt_info,
27+
address: funding_cell.address_hash,
28+
}
2729
end
2830

2931
def closed_transaction_info
3032
return Hash.new unless closed_transaction
3133

32-
closed_transaction.as_json(only: %i[tx_hash block_number block_timestamp]).merge(
34+
{
35+
tx_hash: closed_transaction.tx_hash,
36+
block_number: closed_transaction.block_number,
37+
block_timestamp: open_transaction.block_timestamp,
38+
}.merge(
3339
close_accounts: closed_transaction.outputs.map do |cell|
3440
{
3541
capacity: cell.capacity,
@@ -44,12 +50,6 @@ def udt_info
4450
udt&.as_json(only: %i[full_name symbol decimal icon_file])
4551
end
4652

47-
def funding_cell
48-
open_transaction.outputs.includes(:lock_script).find_by(
49-
lock_scripts: { code_hash: Settings.fiber_funding_code_hash },
50-
)
51-
end
52-
5353
def deleted_at_timestamp
5454
return unless deleted_at
5555

@@ -80,6 +80,8 @@ def deleted_at_timestamp
8080
# deleted_at :datetime
8181
# cell_output_id :bigint
8282
# address_id :bigint
83+
# update_info_of_node1 :jsonb
84+
# update_info_of_node2 :jsonb
8385
#
8486
# Indexes
8587
#

app/models/fiber_graph_node.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ def open_channels_count
3131
end
3232

3333
def last_updated_timestamp
34-
node1_timestamps = FiberGraphChannel.where(node1: node_id).pluck(:last_updated_timestamp_of_node1)
35-
node2_timestamps = FiberGraphChannel.where(node2: node_id).pluck(:last_updated_timestamp_of_node2)
34+
node1_timestamps = FiberGraphChannel.where(node1: node_id).filter_map { _1.update_info_of_node1["timestamp"]&.to_i }
35+
node2_timestamps = FiberGraphChannel.where(node2: node_id).filter_map { _1.update_info_of_node2["timestamp"]&.to_i }
3636
closed_transaction_ids = FiberGraphChannel.where(node1: node_id).
3737
or(FiberGraphChannel.where(node2: node_id)).
3838
where.not(closed_transaction_id: nil).pluck(:closed_transaction_id)

0 commit comments

Comments
 (0)