Skip to content

Commit

Permalink
Merge pull request #2002 from nervosnetwork/testnet
Browse files Browse the repository at this point in the history
Deploy to mainnet
  • Loading branch information
zmcNotafraid authored Jun 27, 2024
2 parents 801feea + 28870b7 commit 8382c4a
Show file tree
Hide file tree
Showing 31 changed files with 551 additions and 74 deletions.
20 changes: 19 additions & 1 deletion app/controllers/api/v1/udts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Api
module V1
class UdtsController < ApplicationController
before_action :validate_query_params, only: :show
before_action :validate_query_params, only: %i[show holder_allocation]
before_action :validate_pagination_params, :pagination_params, only: :index

def index
Expand Down Expand Up @@ -84,6 +84,24 @@ def download_csv
raise Api::V1::Exceptions::UdtNotFoundError
end

def holder_allocation
udt = Udt.find_by!(type_hash: params[:id], published: true)
holder_allocation = udt.udt_holder_allocations.find_by(contract_id: nil)
btc_holder_count = holder_allocation&.btc_holder_count || 0

lock_hashes = udt.udt_holder_allocations.includes(:contract).where.not(contract_id: nil).map do |allocation|
{
name: allocation.contract.name,
code_hash: allocation.contract.code_hash,
holder_count: allocation.ckb_holder_count,
}
end

render json: { btc_holder_count:, lock_hashes: }
rescue ActiveRecord::RecordNotFound
raise Api::V1::Exceptions::UdtNotFoundError
end

private

def validate_query_params
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/api/v2/nft/collections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ def index
if params[:type].present?
scope = scope.where(standard: params[:type])
end
if params[:tags].present?
tags = parse_tags
scope = scope.where("tags @> array[?]::varchar[]", tags) unless tags.empty?
end
pagy, collections = pagy(sort_collections(scope))

render json: {
Expand Down Expand Up @@ -51,6 +55,11 @@ def sort_collections(records)
records.order("#{sort} #{order}")
end
end

def parse_tags
tags = params[:tags].split(",")
tags & TokenCollection::VALID_TAGS
end
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/concerns/cell_data_comparator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def diff_cota_nft_cells(transaction, inputs, outputs)
def diff_normal_nft_cells(inputs, outputs)
transfers = Hash.new { |h, k| h[k] = Array.new }
nft_infos = Hash.new { |h, k| h[k] = nil }
cell_types = %w(m_nft_token nrc_721_token spore_cell m_nft_issuer
cell_types = %w(m_nft_token nrc_721_token spore_cell did_cell m_nft_issuer
m_nft_class nrc_721_factory cota_registry spore_cluster)

process_nft = ->(c, h, o) {
Expand Down Expand Up @@ -136,7 +136,7 @@ def diff_normal_nft_cells(inputs, outputs)

def nft_info(cell)
case cell.cell_type
when "m_nft_token", "nrc_721_token", "spore_cell"
when "m_nft_token", "nrc_721_token", "spore_cell", "did_cell"
item = TokenItem.joins(:type_script).where(type_script: { script_hash: cell.type_hash }).take
{ token_id: item&.token_id, name: item&.collection&.name }
when "m_nft_issuer"
Expand Down
6 changes: 1 addition & 5 deletions app/interactions/rgb_transactions/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ def execute
annotations = BitcoinAnnotation.includes(:ckb_transaction).
where("bitcoin_annotations.tags @> array[?]::varchar[]", ["rgbpp"]).
order(order_by => asc_or_desc)

if leap_direction.present?
annotations = annotations.where("bitcoin_annotations.leap_direction = ?", leap_direction)
end

annotations = annotations.where(leap_direction:) if leap_direction.present?
annotations.page(page).per(page_size)
end

Expand Down
90 changes: 90 additions & 0 deletions app/jobs/import_btc_time_cells_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
class ImportBtcTimeCellsJob < ApplicationJob
queue_as :bitcoin

def perform(cell_ids)
ApplicationRecord.transaction do
cell_outputs = CellOutput.where(id: cell_ids)

utxo_map = build_utxo_map(cell_outputs)
raw_tx_data = fetch_raw_transactions!(utxo_map)
transactions = build_transactions!(cell_outputs, raw_tx_data, utxo_map)

bitcoin_transfers_attributes = []

cell_outputs.each do |cell_output|
txid = utxo_map[cell_output.id]
tx = transactions[txid]

# build transfer
bitcoin_transfers_attributes << {
bitcoin_transaction_id: tx.id,
ckb_transaction_id: cell_output.ckb_transaction_id,
lock_type: "btc_time",
cell_output_id: cell_output.id,
}
rescue StandardError => e
Rails.logger.error("Handle btc time cell (id: #{cell_output.id}) failed: #{e.message}")
end

if bitcoin_transfers_attributes.present?
BitcoinTransfer.upsert_all(bitcoin_transfers_attributes,
unique_by: %i[cell_output_id])
end
end
rescue StandardError => e
Rails.logger.error("ImportBtcTimeCells failed: #{e.message}")
end

def build_utxo_map(cell_outputs)
cell_outputs.each_with_object({}) do |cell_output, data|
parsed_args = CkbUtils.parse_btc_time_lock_cell(cell_output.lock_script.args)
data[cell_output.id] = parsed_args.txid
end
end

def fetch_raw_transactions!(utxo_map)
txids = utxo_map.values.uniq

raw_tx_data = {}
txids.each do |txid|
data = Rails.cache.read(txid)
unless data
data = rpc.getrawtransaction(txid, 2)
Rails.cache.write(txid, data, expires_in: 30.minutes)
end
raw_tx_data[txid] = data["result"]
rescue StandardError => e
Rails.logger.error("Failed to fetch transaction #{txid}: #{e.message}")
end

raw_tx_data
end

def build_transactions!(cell_outputs, raw_tx_data, utxo_map)
transaction_attributes = []

cell_outputs.each do |cell_output|
txid = utxo_map[cell_output.id]
raw_tx = raw_tx_data[txid]

next unless raw_tx

created_at = Time.at((cell_output.block_timestamp / 1000).to_i).in_time_zone
transaction_attributes << {
txid: raw_tx["txid"],
tx_hash: raw_tx["hash"],
time: raw_tx["time"],
block_hash: raw_tx["blockhash"],
block_height: 0,
created_at:,
}
end

BitcoinTransaction.upsert_all(transaction_attributes, unique_by: :txid)
BitcoinTransaction.where(txid: transaction_attributes.map { _1[:txid] }).index_by(&:txid)
end

def rpc
@rpc ||= Bitcoin::Rpc.instance
end
end
196 changes: 196 additions & 0 deletions app/jobs/import_rgbpp_cells_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
class ImportRgbppCellsJob < ApplicationJob
class MissingVoutError < StandardError; end
class MissingAddressError < StandardError; end

queue_as :bitcoin

def perform(cell_ids)
ApplicationRecord.transaction do
cell_outputs = CellOutput.where(id: cell_ids)

utxo_map = build_utxo_map(cell_outputs)
raw_tx_data = fetch_raw_transactions!(utxo_map)
transactions = build_transactions!(cell_outputs, raw_tx_data, utxo_map)

vout_attributes = []
op_returns_attributes = []
vin_attributes = []
bitcoin_transfers_attributes = []

cell_outputs.each do |cell_output|
utxo = utxo_map[cell_output.id]
txid = utxo[:txid]
out_index = utxo[:out_index]

raw_tx = raw_tx_data[txid]
tx = transactions[txid]

next unless raw_tx && tx

# build op_returns
op_returns = build_op_returns!(raw_tx, tx, cell_output.ckb_transaction)
op_returns_attributes.concat(op_returns) if op_returns.present?

# build vouts
vout = build_vout!(raw_tx, tx, out_index, cell_output)
vout_attributes << vout if vout.present?

# build vin
vin = build_vin!(cell_output.id, tx)
vin_attributes << vin if vin.present?

# build transfer
bitcoin_transfers_attributes << {
bitcoin_transaction_id: tx.id,
ckb_transaction_id: cell_output.ckb_transaction_id,
lock_type: "rgbpp",
cell_output_id: cell_output.id,
}

rescue StandardError => e
Rails.logger.error("Handle rgbpp cell (id: #{cell_output.id}) failed: #{e.message}")
end

if vout_attributes.present?
BitcoinVout.upsert_all(vout_attributes,
unique_by: %i[bitcoin_transaction_id index cell_output_id])
end

if vin_attributes.present?
BitcoinVin.upsert_all(vin_attributes,
unique_by: %i[ckb_transaction_id cell_input_id])
end

if op_returns_attributes.present?
BitcoinVout.upsert_all(op_returns_attributes,
unique_by: %i[bitcoin_transaction_id index cell_output_id])
end

if bitcoin_transfers_attributes.present?
BitcoinTransfer.upsert_all(bitcoin_transfers_attributes,
unique_by: %i[cell_output_id])
end
end
rescue StandardError => e
Rails.logger.error("ImportRgbppCells failed: #{e.message}")
end

def build_utxo_map(cell_outputs)
cell_outputs.each_with_object({}) do |cell_output, data|
txid, out_index = CkbUtils.parse_rgbpp_args(cell_output.lock_script.args)
data[cell_output.id] = { txid:, out_index: }
end
end

def fetch_raw_transactions!(utxo_map)
txids = utxo_map.values.map { _1[:txid] }.uniq

raw_tx_data = {}
txids.each do |txid|
data = Rails.cache.read(txid)
unless data
data = rpc.getrawtransaction(txid, 2)
Rails.cache.write(txid, data, expires_in: 30.minutes)
end
raw_tx_data[txid] = data["result"]
rescue StandardError => e
Rails.logger.error("Failed to fetch transaction #{txid}: #{e.message}")
end

raw_tx_data
end

def build_transactions!(cell_outputs, raw_tx_data, utxo_map)
transaction_attributes = []

cell_outputs.each do |cell_output|
txid = utxo_map[cell_output.id][:txid]
raw_tx = raw_tx_data[txid]

next unless raw_tx

created_at = Time.at((cell_output.block_timestamp / 1000).to_i).in_time_zone
transaction_attributes << {
txid: raw_tx["txid"],
tx_hash: raw_tx["hash"],
time: raw_tx["time"],
block_hash: raw_tx["blockhash"],
block_height: 0,
created_at:,
}
end

BitcoinTransaction.upsert_all(transaction_attributes, unique_by: :txid)
BitcoinTransaction.where(txid: transaction_attributes.map { _1[:txid] }).index_by(&:txid)
end

def build_op_returns!(raw_tx, tx, ckb_tx)
raw_tx["vout"].map do |vout|
data = vout.dig("scriptPubKey", "hex")
script_pubkey = Bitcoin::Script.parse_from_payload(data.htb)
next unless script_pubkey.op_return?

{
bitcoin_transaction_id: tx.id,
data:,
index: vout.dig("n"),
asm: vout.dig("scriptPubKey", "asm"),
op_return: true,
ckb_transaction_id: ckb_tx.id,
}
end.compact
end

def build_vout!(raw_tx, tx, out_index, cell_output)
vout = raw_tx["vout"].find { _1["n"] == out_index }
raise MissingVoutError, "Missing vout txid: #{raw_tx['txid']} index: #{out_index}" unless vout

address_hash = vout.dig("scriptPubKey", "address")
raise MissingAddressError, "Missing vout address: #{raw_tx['txid']} index: #{out_index}" unless address_hash

address = build_address!(address_hash, cell_output)
{
bitcoin_transaction_id: tx.id,
bitcoin_address_id: address.id,
data: vout.dig("scriptPubKey", "hex"),
index: vout.dig("n"),
asm: vout.dig("scriptPubKey", "asm"),
op_return: false,
ckb_transaction_id: cell_output.ckb_transaction_id,
cell_output_id: cell_output.id,
address_id: cell_output.address_id,
}
end

def build_address!(address_hash, cell_output)
created_at = Time.at((cell_output.block_timestamp / 1000).to_i).in_time_zone
bitcoin_address = BitcoinAddress.create_with(created_at:).find_or_create_by!(address_hash:)
BitcoinAddressMapping.
create_with(bitcoin_address_id: bitcoin_address.id).
find_or_create_by!(ckb_address_id: cell_output.address_id)

bitcoin_address
end

def build_vin!(cell_id, tx)
cell_input = CellInput.find_by(previous_cell_output_id: cell_id)
previous_vout = BitcoinVout.find_by(cell_output_id: cell_id)
return unless cell_input && previous_vout

previous_cell_output = cell_input.output
# check whether previous_cell_output utxo consumed
if previous_cell_output.dead? && previous_vout.binding?
previous_vout.update!(status: "normal", consumed_by_id: tx.id)
end

{
previous_bitcoin_vout_id: previous_vout.id,
ckb_transaction_id: cell_input.ckb_transaction_id,
cell_input_id: cell_input.id,
}
end

def rpc
@rpc ||= Bitcoin::Rpc.instance
end
end
6 changes: 1 addition & 5 deletions app/jobs/revert_block_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,7 @@ def recalculate_udt_accounts(udt_type_hashes, local_tip_block)
when "xudt", "omiga_inscription", "xudt_compatible"
amount = address.cell_outputs.live.where(cell_type: udt_account.udt_type).where(type_hash:).sum(:udt_amount)
udt_account.update!(amount:)
when "m_nft_token"
udt_account.destroy
when "nrc_721_token"
udt_account.destroy
when "spore_cell"
when "m_nft_token", "nrc_721_token", "spore_cell", "did_cell"
udt_account.destroy
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/cell_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CellInput < ApplicationRecord
normal: 0, nervos_dao_deposit: 1, nervos_dao_withdrawing: 2, udt: 3, m_nft_issuer: 4,
m_nft_class: 5, m_nft_token: 6, nrc_721_token: 7, nrc_721_factory: 8, cota_registry: 9,
cota_regular: 10, spore_cluster: 11, spore_cell: 12, omiga_inscription_info: 13, omiga_inscription: 14,
xudt: 15, unique_cell: 16, xudt_compatible: 17
xudt: 15, unique_cell: 16, xudt_compatible: 17, did_cell: 18
}

def output
Expand Down
Loading

0 comments on commit 8382c4a

Please sign in to comment.