Skip to content

Commit

Permalink
Merge pull request #1826 from nervosnetwork/develop
Browse files Browse the repository at this point in the history
Deploy to testnet
  • Loading branch information
rabbitz committed May 7, 2024
2 parents c49ae8e + 49f41ca commit 4e79252
Show file tree
Hide file tree
Showing 26 changed files with 534 additions and 128 deletions.
10 changes: 10 additions & 0 deletions app/controllers/api/v1/xudts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ def download_csv
raise Api::V1::Exceptions::UdtNotFoundError
end

def snapshot
args = params.permit(:id, :number)
file = CsvExportable::ExportUdtSnapshotJob.perform_now(args.to_h)

Check warning on line 53 in app/controllers/api/v1/xudts_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v1/xudts_controller.rb#L52-L53

Added lines #L52 - L53 were not covered by tests

send_data file, type: "text/csv; charset=utf-8; header=present",

Check warning on line 55 in app/controllers/api/v1/xudts_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v1/xudts_controller.rb#L55

Added line #L55 was not covered by tests
disposition: "attachment;filename=xudt_snapshot.csv"
rescue ActiveRecord::RecordNotFound
raise Api::V1::Exceptions::UdtNotFoundError

Check warning on line 58 in app/controllers/api/v1/xudts_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v1/xudts_controller.rb#L58

Added line #L58 was not covered by tests
end

private

def validate_query_params
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v2/bitcoin_transactions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def query

render json: res
rescue StandardError => e
Rails.logger.error "get raw transaction(#{params[:txids]}) failed: #{e.message}"
Rails.logger.error "get raw transactions(#{params[:txids]}) failed: #{e.message}"

Check warning on line 25 in app/controllers/api/v2/bitcoin_transactions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v2/bitcoin_transactions_controller.rb#L25

Added line #L25 was not covered by tests
render json: {}, status: :not_found
end

Expand Down
16 changes: 9 additions & 7 deletions app/controllers/api/v2/ckb_transactions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,18 @@ def rgb_digest
end
end

bitcoin_transaction = BitcoinTransaction.includes(:bitcoin_vouts).find_by(

Check warning on line 66 in app/controllers/api/v2/ckb_transactions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v2/ckb_transactions_controller.rb#L66

Added line #L66 was not covered by tests
bitcoin_vouts: { ckb_transaction_id: @ckb_transaction.id },
)
op_return = @ckb_transaction.bitcoin_vouts.find_by(op_return: true)
bitcoin_transaction = BitcoinTransaction.includes(:bitcoin_vouts).find_by(bitcoin_vouts: { ckb_transaction_id: @ckb_transaction.id })
if op_return && bitcoin_transaction
txid = bitcoin_transaction.txid
commitment = op_return.commitment
confirmations = bitcoin_transaction.confirmations

Check warning on line 73 in app/controllers/api/v2/ckb_transactions_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/api/v2/ckb_transactions_controller.rb#L70-L73

Added lines #L70 - L73 were not covered by tests
end

render json: {
data: {
txid: bitcoin_transaction&.txid,
confirmations: bitcoin_transaction&.confirmations,
commitment: op_return&.commitment,
transfers:,
},
data: { txid:, confirmations:, commitment:, transfers: },
}
end

Expand Down
41 changes: 23 additions & 18 deletions app/controllers/api/v2/statistics_controller.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
module Api::V2
class StatisticsController < BaseController
def transaction_fees
expires_in 15.seconds, public: true
stats_info = StatisticInfo.default
if stale?(stats_info)
expires_in 15.seconds, public: true

render json: {
transaction_fee_rates: stats_info.transaction_fee_rates,
pending_transaction_fee_rates: stats_info.pending_transaction_fee_rates,
last_n_days_transaction_fee_rates: stats_info.last_n_days_transaction_fee_rates,
}
render json: {
transaction_fee_rates: stats_info.transaction_fee_rates,
pending_transaction_fee_rates: stats_info.pending_transaction_fee_rates,
last_n_days_transaction_fee_rates: stats_info.last_n_days_transaction_fee_rates,
}
end
end

def contract_resource_distributed
expires_in 30.minutes, public: true
contracts = Contract.filter_nil_hash_type
if stale?(contracts)
expires_in 30.minutes, public: true
json = contracts.map do |contract|
{
name: contract.name,
code_hash: contract.code_hash,
hash_type: contract.hash_type,
tx_count: contract.ckb_transactions_count,
h24_tx_count: contract.h24_ckb_transactions_count,
ckb_amount: (contract.total_referring_cells_capacity / 10**8).truncate(8),
address_count: contract.addresses_count,
}
end

json = Contract.filter_nil_hash_type.map do |contract|
{
name: contract.name,
code_hash: contract.code_hash,
hash_type: contract.hash_type,
tx_count: contract.ckb_transactions_count,
ckb_amount: (contract.total_referring_cells_capacity / 10**8).truncate(8),
address_count: contract.addresses_count,
}
render json:
end

render json:
end
end
end
58 changes: 58 additions & 0 deletions app/jobs/csv_exportable/export_udt_snapshot_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module CsvExportable
class ExportUdtSnapshotJob < BaseExporter
attr_accessor :udt, :block

def perform(args)
@block = Block.find_by!(number: args[:number])
@udt = Udt.published_xudt.find_by!(type_hash: args[:id])
type_script = TypeScript.find_by(@udt.type_script)

condition = <<-SQL
type_script_id = #{type_script.id} AND
block_timestamp <= #{@block.timestamp} AND
(consumed_block_timestamp > #{@block.timestamp} OR consumed_block_timestamp IS NULL)
SQL
snapshot = CellOutput.where(condition).group(:address_id).sum(:udt_amount)

rows = []
snapshot = snapshot.reject { |_, v| v.to_f.zero? }
snapshot.sort_by { |_k, v| -v }.each do |address_id, udt_amount|
row = generate_row(address_id, udt_amount)
next if row.blank?

rows << row
end

header = ["Token Symbol", "Block Height", "UnixTimestamp", "date(UTC)", "Address", "Amount"]
generate_csv(header, rows)
end

def generate_row(address_id, udt_amount)
address = Address.find_by(id: address_id)
return unless address

address_hash = address.bitcoin_address&.address_hash || address.address_hash
datetime = datetime_utc(@block.timestamp)

if (decimal = @udt.decimal)
[
@udt.symbol,
@block.number,
@block.timestamp,
datetime,
address_hash,
parse_udt_amount(udt_amount.to_d, decimal),
]
else
[
@udt.symbol,
@block.number,
@block.timestamp,
datetime,
address_hash,
"#{udt_amount} (raw)",
]
end
end
end
end
58 changes: 58 additions & 0 deletions app/jobs/import_btc_time_cell_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
class ImportBtcTimeCellJob < ApplicationJob
queue_as :bitcoin

def perform(cell_id)
ApplicationRecord.transaction do
cell_output = CellOutput.find_by(id: cell_id)
return unless cell_output

lock_script = cell_output.lock_script
return unless CkbUtils.is_btc_time_lock_cell?(lock_script)

parsed_args = CkbUtils.parse_btc_time_lock_cell(lock_script.args)
txid = parsed_args.txid
Rails.logger.info("Importing btc time cell #{cell_id} txid #{txid}")

# build bitcoin transaction
raw_tx = fetch_raw_transaction(txid)
return unless raw_tx

tx = build_transaction!(raw_tx)
# build transfer
BitcoinTransfer.create_with(
bitcoin_transaction_id: tx.id,
ckb_transaction_id: cell_output.ckb_transaction_id,
lock_type: "btc_time",
).find_or_create_by!(
cell_output_id: cell_id,
)
end
end

def build_transaction!(raw_tx)
tx = BitcoinTransaction.find_by(txid: raw_tx["txid"])
return tx if tx

BitcoinTransaction.create!(
txid: raw_tx["txid"],
tx_hash: raw_tx["hash"],
time: raw_tx["time"],
block_hash: raw_tx["blockhash"],
block_height: 0,
)
end

def fetch_raw_transaction(txid)
data = Rails.cache.read(txid)
data ||= rpc.getrawtransaction(txid, 2)
Rails.cache.write(txid, data, expires_in: 10.minutes) unless Rails.cache.exist?(txid)
data["result"]
rescue StandardError => e
Rails.logger.error "get bitcoin raw transaction #{txid} failed: #{e}"
nil
end

def rpc
@rpc ||= Bitcoin::Rpc.instance
end
end
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@
class ImportBitcoinUtxoJob < ApplicationJob
class ImportRgbppCellJob < ApplicationJob
queue_as :bitcoin

def perform(cell_id)
ApplicationRecord.transaction do
cell_output = CellOutput.find_by(id: cell_id)
unless cell_output
raise ArgumentError, "Missing cell_output(#{cell_id})"
end
return unless cell_output

lock_script = cell_output.lock_script
return unless CkbUtils.is_rgbpp_lock_cell?(lock_script)

txid, out_index = CkbUtils.parse_rgbpp_args(lock_script.args)
Rails.logger.info("Importing bitcoin utxo #{txid} out_index #{out_index}")
vout_attributes = []
Rails.logger.info("Importing rgbpp cell #{cell_id} txid #{txid} out_index #{out_index}")

# build bitcoin transaction
raw_tx = rpc.getrawtransaction(txid, 2)
raw_tx = fetch_raw_transaction(txid)
return unless raw_tx

tx = build_transaction!(raw_tx)
# build op_returns
op_returns = build_op_returns!(raw_tx, tx, cell_output.ckb_transaction, vout_attributes)
vout_attributes = []
op_returns = build_op_returns!(raw_tx, tx, cell_output.ckb_transaction)
vout_attributes.concat(op_returns) if op_returns.present?
# build vout
# build vouts
vout_attributes << build_vout!(raw_tx, tx, out_index, cell_output)

if vout_attributes.present?
BitcoinVout.upsert_all(
vout_attributes,
unique_by: %i[bitcoin_transaction_id index cell_output_id],
)
end
# build vin
cell_input = CellInput.find_by(previous_cell_output_id: cell_id)
previous_vout = BitcoinVout.find_by(cell_output_id: cell_id)
if cell_input && previous_vout
BitcoinVin.create_with(
previous_bitcoin_vout_id: previous_vout.id,
).find_or_create_by!(
ckb_transaction_id: cell_input.ckb_transaction_id,
cell_input_id: cell_input.id,
)
end
# build transfer
BitcoinTransfer.create_with(
bitcoin_transaction_id: tx.id,
ckb_transaction_id: cell_output.ckb_transaction_id,
lock_type: "rgbpp",
).find_or_create_by!(
cell_output_id: cell_id,
)
end
end

Expand All @@ -49,18 +68,18 @@ def build_transaction!(raw_tx)
)
end

def build_op_returns!(raw_tx, tx, ckb_tx, v_attributes)
def build_op_returns!(raw_tx, tx, ckb_tx)
op_returns = []

raw_tx["vout"].each do |vout|
data = vout.dig("scriptPubKey", "hex")
script_pubkey = Bitcoin::Script.parse_from_payload(data.htb)
next unless script_pubkey.op_return?

# commiment = script_pubkey.op_return_data.bth
# unless commiment == CkbUtils.calculate_commitment(ckb_tx.tx_hash)
# raise ArgumentError, "Invalid commitment found in the CKB VirtualTx"
# end
# commiment = script_pubkey.op_return_data.bth
# unless commiment == CkbUtils.calculate_commitment(ckb_tx.tx_hash)
# raise ArgumentError, "Invalid commitment found in the CKB VirtualTx"
# end

op_return = {
bitcoin_transaction_id: tx.id,
Expand All @@ -74,7 +93,12 @@ def build_op_returns!(raw_tx, tx, ckb_tx, v_attributes)
address_id: nil,
}

op_returns << op_return if v_attributes.exclude?(op_return)
next if BitcoinVout.exists?(
bitcoin_transaction_id: op_return[:bitcoin_transaction_id],
index: op_return[:index],
)

op_returns << op_return
end

op_returns
Expand Down Expand Up @@ -110,6 +134,16 @@ def build_address!(address_hash, cell_output)
bitcoin_address
end

def fetch_raw_transaction(txid)
data = Rails.cache.read(txid)
data ||= rpc.getrawtransaction(txid, 2)
Rails.cache.write(txid, data, expires_in: 10.minutes) unless Rails.cache.exist?(txid)
data["result"]
rescue StandardError => e
Rails.logger.error "get bitcoin raw transaction #{txid} failed: #{e}"
nil
end

def rpc
@rpc ||= Bitcoin::Rpc.instance
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/bitcoin_address_mapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ class BitcoinAddressMapping < ApplicationRecord
#
# Indexes
#
# idex_bitcon_addresses_on_mapping (bitcoin_address_id,ckb_address_id) UNIQUE
# index_bitcoin_addresses_on_mapping (bitcoin_address_id,ckb_address_id) UNIQUE
#
6 changes: 3 additions & 3 deletions app/models/bitcoin_transaction.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
class BitcoinTransaction < ApplicationRecord
has_many :bitcoin_vouts
has_many :bitcoin_transfers

def confirmations
tip_block_height =
Rails.cache.fetch("tip_block_height", expires_in: 5.minutes) do
blocks = Bitcoin::Rpc.instance.getchaintips
tip_block = blocks.find { |h| h["status"] == "active" }
tip_block["height"]
chain_info = Bitcoin::Rpc.instance.getblockchaininfo
chain_info["headers"]

Check warning on line 9 in app/models/bitcoin_transaction.rb

View check run for this annotation

Codecov / codecov/patch

app/models/bitcoin_transaction.rb#L8-L9

Added lines #L8 - L9 were not covered by tests
rescue StandardError => e
Rails.logger.error "get tip block faild: #{e.message}"
nil
Expand Down
26 changes: 26 additions & 0 deletions app/models/bitcoin_transfer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class BitcoinTransfer < ApplicationRecord
belongs_to :bitcoin_transaction
belongs_to :ckb_transaction
belongs_to :cell_output

Check warning on line 4 in app/models/bitcoin_transfer.rb

View check run for this annotation

Codecov / codecov/patch

app/models/bitcoin_transfer.rb#L1-L4

Added lines #L1 - L4 were not covered by tests

enum lock_type: { rgbpp: 0, btc_time: 1 }
end

Check warning on line 7 in app/models/bitcoin_transfer.rb

View check run for this annotation

Codecov / codecov/patch

app/models/bitcoin_transfer.rb#L6-L7

Added lines #L6 - L7 were not covered by tests

# == Schema Information
#
# Table name: bitcoin_transfers
#
# id :bigint not null, primary key
# bitcoin_transaction_id :bigint
# ckb_transaction_id :bigint
# cell_output_id :bigint
# lock_type :integer default("rgbpp")
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_bitcoin_transfers_on_bitcoin_transaction_id (bitcoin_transaction_id)
# index_bitcoin_transfers_on_cell_output_id (cell_output_id) UNIQUE
# index_bitcoin_transfers_on_ckb_transaction_id (ckb_transaction_id)
#
Loading

0 comments on commit 4e79252

Please sign in to comment.