Skip to content

Commit

Permalink
Merge pull request #1803 from nervosnetwork/develop
Browse files Browse the repository at this point in the history
Deploy to testnet
  • Loading branch information
rabbitz authored Apr 20, 2024
2 parents 1efaf72 + e4a7ddd commit fffb89f
Show file tree
Hide file tree
Showing 23 changed files with 155 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CKB_WS_URL="http://localhost:28114"
# database config items
#DB_USERNAME="postgres"
#DB_PASSWORD="postgres"
#DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
DATABASE_URL="postgres://postgres:postgres@localhost:5432/ckb_explorer_dev"

# (optional)
# database pool, default is 5.
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ gem "sentry-sidekiq"

gem "bigdecimal"

gem "nio4r", "> 2.5.8"

# Deployment
gem "rack-attack"

Expand Down Expand Up @@ -107,7 +109,6 @@ group :development do
gem "rubocop", require: false
gem "rubocop-performance", require: false
gem "rubocop-rails", require: false
gem "solargraph"
gem "spring"
gem "spring-watcher-listen"
end
Expand Down
34 changes: 2 additions & 32 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,9 @@ GEM
protocol-rack (~> 0.1)
protocol-websocket (~> 0.9.1)
awesome_print (1.9.2)
backport (1.2.0)
base32 (0.3.4)
bech32 (1.4.2)
thor (>= 1.1.0)
benchmark (0.2.1)
bigdecimal (3.1.4)
bip-schnorr (0.7.0)
ecdsa_ext (~> 0.5.0)
Expand Down Expand Up @@ -159,7 +157,6 @@ GEM
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
deep_merge (1.2.2)
diff-lcs (1.5.0)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
docile (1.4.0)
Expand Down Expand Up @@ -201,7 +198,6 @@ GEM
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
e2mmap (0.1.0)
ecdsa (1.2.0)
ecdsa_ext (0.5.1)
ecdsa (~> 1.2.0)
Expand Down Expand Up @@ -248,7 +244,6 @@ GEM
concurrent-ruby (~> 1.0)
iniparse (1.5.0)
io-event (1.1.6)
jaro_winkler (1.5.4)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
Expand All @@ -267,10 +262,6 @@ GEM
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
kredis (1.3.0.1)
activesupport (>= 6.0.0)
redis (>= 4.2, < 6)
Expand Down Expand Up @@ -311,7 +302,7 @@ GEM
net-smtp (0.3.3)
net-protocol
newrelic_rpm (8.12.0)
nio4r (2.5.8)
nio4r (2.7.1)
nokogiri (1.14.3)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
Expand Down Expand Up @@ -394,7 +385,6 @@ GEM
ffi (~> 1.0)
rbnacl (7.1.1)
ffi
rbs (2.8.4)
rbsecp256k1 (5.1.1)
mini_portile2 (~> 2.8)
pkg-config (~> 1.5)
Expand All @@ -406,8 +396,6 @@ GEM
redis-objects (2.0.0.beta)
redis (~> 5.0)
regexp_parser (2.7.0)
reverse_markdown (2.1.1)
nokogiri
rexml (3.2.5)
rubocop (1.50.1)
json (~> 2.3)
Expand Down Expand Up @@ -459,28 +447,11 @@ GEM
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
siphash (0.0.1)
solargraph (0.49.0)
backport (~> 1.2)
benchmark
bundler (~> 2.0)
diff-lcs (~> 1.4)
e2mmap
jaro_winkler (~> 1.5)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.1)
parser (~> 3.0)
rbs (~> 2.0)
reverse_markdown (~> 2.0)
rubocop (~> 1.38)
thor (~> 1.0)
tilt (~> 2.0)
yard (~> 0.9, >= 0.9.24)
spring (4.1.0)
spring-watcher-listen (2.1.0)
listen (>= 2.7, < 4.0)
spring (>= 4)
thor (1.2.1)
tilt (2.1.0)
timeout (0.3.0)
timers (4.3.5)
traces (0.8.0)
Expand All @@ -500,7 +471,6 @@ GEM
websocket-extensions (0.1.5)
with_advisory_lock (4.6.0)
activerecord (>= 4.2)
yard (0.9.34)
zeitwerk (2.6.6)

PLATFORMS
Expand Down Expand Up @@ -544,6 +514,7 @@ DEPENDENCIES
net-pop
net-smtp
newrelic_rpm
nio4r (> 2.5.8)
nokogiri (>= 1.11.0.rc4)
pagy
pg (>= 0.18, < 2.0)
Expand All @@ -570,7 +541,6 @@ DEPENDENCIES
sidekiq
simplecov
simplecov-cobertura
solargraph
spring
spring-watcher-listen
tzinfo-data
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
## About CKB

CKB is the layer 1 of Nervos Network, a public/permissionless blockchain. CKB uses [Proof of Work](https://en.wikipedia.org/wiki/Proof-of-work_system) and [improved Nakamoto concensus](https://medium.com/nervosnetwork/breaking-the-throughput-limit-of-nakamoto-consensus-ccdf65fe0832) to achieve maximized performance on average hardware and internet condition, without sacrificing decentralization and security which are the core value of blockchain.

# CKB Explorer

CKB Explorer is a [Nervos CKB](https://github.com/nervosnetwork/ckb) blockchain explorer built with React and Ruby on Rails.

It supports searching block, transaction, address and includes two parts: [frontend](https://github.com/nervosnetwork/ckb-explorer-frontend)
and [backend server](https://github.com/nervosnetwork/ckb-explorer) (this project).

# CKB Explorer Server

[![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/nervosnetwork/ckb-explorer/blob/develop/COPYING)
[![TravisCI](https://travis-ci.com/nervosnetwork/ckb-explorer.svg?branch=develop)](https://travis-ci.com/nervosnetwork/ckb-explorer)
[![Codecov](https://codecov.io/gh/nervosnetwork/ckb-explorer/branch/master/graph/badge.svg)](https://codecov.io/gh/nervosnetwork/ckb-explorer/branch/master)
Expand All @@ -19,16 +22,15 @@ A blockchain explorer service of [Nervos CKB](https://github.com/nervosnetwork/c

- [PostgreSQL](https://www.postgresql.org/) 14.3
- [Redis](https://redis.io/) 6+
- [libsodium](https://libsodium.gitbook.io/doc/installation)
- [secp256k1](https://github.com/bitcoin-core/secp256k1.git)
- [libsodium](https://github.com/nervosnetwork/ckb-sdk-ruby?tab=readme-ov-file#prerequisites)
- [secp256k1](https://github.com/nervosnetwork/ckb-sdk-ruby?tab=readme-ov-file#prerequisites)

## Initial Project

```shell
$ cd ckb-explorer/
$ cp .env.example .env # (in this config file, please modify the items according to your local environment)
$ touch .env.local # (overwrite `.env` config if you need in `.env.local`, such as DB_USERNAME, DB_PASSWORD...)
$ touch config/settings.local.yml # (overwrite `config/settings.yml` to config available hosts)
$ touch .env.local # (overwrite `.env` config if you need in `.env.local`, such as DATABASE_URL,CKB_NODE_URL...)
$ bin/setup
```

Expand Down Expand Up @@ -83,6 +85,7 @@ $ docker compose up -d
```

## How to Contribute

CKB Explorer Server is an open source project and your contribution is very much appreciated. Please check out
[CONTRIBUTING.md](CONTRIBUTING.md) for guidelines about how to proceed.

Expand All @@ -96,4 +99,3 @@ API reference has been migrated to [CKB Explorer on ReadMe](https://ckb-explorer

CKB Explorer is released under the terms of the MIT license. See [COPYING](COPYING) for more information or see
[https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT).

5 changes: 5 additions & 0 deletions app/jobs/import_bitcoin_utxo_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ def build_op_returns!(raw_tx, tx, ckb_tx, v_attributes)
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

op_return = {
bitcoin_transaction_id: tx.id,
bitcoin_address_id: nil,
Expand Down
4 changes: 4 additions & 0 deletions app/models/ckb_sync/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def rgbpp_code_hash
Settings.rgbpp_code_hash
end

def btc_time_code_hash
Settings.btc_time_code_hash
end

METHOD_NAMES.each do |name|
define_method name do |*params|
call_rpc(name, params:)
Expand Down
18 changes: 15 additions & 3 deletions app/models/concerns/ckb_transactions/bitcoin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ def rgb_transaction?
!!tags&.include?("rgbpp")
end

def btc_time_transaction?
is_btc_time_lock_cell = ->(lock_script) { CkbUtils.is_btc_time_lock_cell?(lock_script) }
inputs.includes(:lock_script).any? { is_btc_time_lock_cell.call(_1.lock_script) } ||
outputs.includes(:lock_script).any? { is_btc_time_lock_cell.call(_1.lock_script) }
end

def rgb_commitment
return unless rgb_transaction?

Expand All @@ -20,9 +26,15 @@ def rgb_commitment
end

def rgb_txid
return unless rgb_transaction?

bitcoin_transaction&.txid
if rgb_transaction?
bitcoin_transaction&.txid
elsif btc_time_transaction?
btc_time_lock_cell =
inputs.includes(:lock_script).find_by(lock_scripts: { code_hash: CkbSync::Api.instance.btc_time_code_hash }) ||
outputs.includes(:lock_script).find_by(lock_scripts: { code_hash: CkbSync::Api.instance.btc_time_code_hash })
parsed_args = CkbUtils.parse_btc_time_lock_cell(btc_time_lock_cell.lock_script.args)
parsed_args.txid
end
end

def leap_direction
Expand Down
4 changes: 4 additions & 0 deletions app/serializers/ckb_transaction_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class CkbTransactionSerializer
object.rgb_transaction?
end

attribute :is_btc_time_lock do |object|
object.btc_time_transaction?
end

attribute :rgb_txid do |object|
object.rgb_txid
end
Expand Down
4 changes: 4 additions & 0 deletions app/serializers/ckb_transactions_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ class CkbTransactionsSerializer
object.rgb_transaction?
end

attribute :is_btc_time_lock do |object|
object.btc_time_transaction?
end

attribute :rgb_txid do |object|
object.rgb_txid
end
Expand Down
75 changes: 75 additions & 0 deletions app/utils/ckb_utils.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class CkbUtils
# The block reward halves approximately every 4 years, one epoch is about 4 hours
HALVING_EPOCH = 4 * 365 * 24 / 4
MAX_RGBPP_CELL_NUM = 255

def self.int_to_hex(i)
"0x#{i.to_s(16)}"
Expand Down Expand Up @@ -651,6 +652,38 @@ def self.is_rgbpp_lock_cell?(lock_script)
lock_script.code_hash == CkbSync::Api.instance.rgbpp_code_hash && lock_script.hash_type == "type"
end

def self.is_btc_time_lock_cell?(lock_script)
lock_script.code_hash == CkbSync::Api.instance.btc_time_code_hash && lock_script.hash_type == "type"
end

def self.parse_btc_time_lock_cell(args)
args_serialization = [args.delete_prefix("0x")].pack("H*")
script_offset = [args_serialization[4..7].unpack1("H*")].pack("H*").unpack1("V")
after_offset = [args_serialization[8..11].unpack1("H*")].pack("H*").unpack1("V")
txid_offset = [args_serialization[12..15].unpack1("H*")].pack("H*").unpack1("V")

script_serialization = args_serialization[script_offset...after_offset]
code_hash_offset = [script_serialization[4..7].unpack1("H*")].pack("H*").unpack1("V")
hash_type_offset = [script_serialization[8..11].unpack1("H*")].pack("H*").unpack1("V")
args_offset = [script_serialization[12..15].unpack1("H*")].pack("H*").unpack1("V")
script_code_hash_serialization = script_serialization[code_hash_offset...hash_type_offset]
script_hash_type_serialization = script_serialization[hash_type_offset...args_offset]
script_args_serialization = script_serialization[hash_type_offset + 1..]
code_hash = "0x#{script_code_hash_serialization.unpack1('H*')}"
hash_type_hex = "0x#{script_hash_type_serialization.unpack1('H*')}"
hash_type = hash_type_hex == "0x00" ? "data" : "type"
args = "0x#{script_args_serialization.unpack1('H*')}"
lock = CKB::Types::Script.new(code_hash:, args:, hash_type:)

after_serialization = args_serialization[after_offset...txid_offset]
after = [after_serialization.unpack1("H*")].pack("H*").unpack1("V")

txid_serialization = args_serialization[txid_offset..]
txid = txid_serialization.unpack1("H*").scan(/../).reverse.join

OpenStruct.new(lock:, after:, txid:)
end

# * https://learnmeabitcoin.com/technical/general/byte-order/
# Whenever you're working with transaction/block hashes internally (e.g. inside raw bitcoin data), you use the natural byte order.
# Whenever you're displaying or searching for transaction/block hashes, you use the reverse byte order.
Expand All @@ -662,6 +695,48 @@ def self.parse_rgbpp_args(args)
[txid, out_index]
end

# https://github.com/ckb-cell/rgbpp-sdk/blob/develop/packages/ckb/src/utils/rgbpp.ts#L58-L87
def self.calculate_commitment(tx_hash)
transaction = CkbTransaction.fetch_sdk_transaction(tx_hash)

hash = Digest::SHA256.new
hash.update("RGB++")
version = [0, 0].pack("C*")
hash.update(version)

if transaction.inputs.length > MAX_RGBPP_CELL_NUM || transaction.outputs.length > MAX_RGBPP_CELL_NUM
raise ArgumentError, "The inputs or outputs length of RGB++ CKB virtual tx cannot be greater than 255"
end

hash.update([transaction.inputs.length, transaction.outputs.length].pack("C*"))

transaction.inputs.each do |input|
out_point = input.previous_output
binary_out_point = CKB::Utils.hex_to_bin(CKB::Serializers::OutPointSerializer.new(out_point).serialize)
hash.update(binary_out_point.bytes.pack("C*"))
end

transaction.outputs.each_with_index do |output, index|
# Before a Bitcoin transaction is confirmed on the blockchain, its transaction ID (txid) is uncertain.
# Therefore, when passing parameters to `calculateCommitment`, manually replace the txid part in the lock args with "0x01000....0000".
output.lock.args = "0x010000000000000000000000000000000000000000000000000000000000000000000000"

binary_output = CKB::Utils.hex_to_bin(CKB::Serializers::OutputSerializer.new(output).serialize)
hash.update(binary_output.bytes.pack("C*"))

output_data = transaction.outputs_data[index]
output_data_serializer = CKB::Serializers::OutputDataSerializer.new(output_data)
output_data_length = output_data_serializer.as_json["items_count"]
binary_output_data_length = CKB::Utils.hex_to_bin("0x#{[output_data_length].pack('V').unpack1('H*')}")
hash.update(binary_output_data_length.bytes.pack("C*"))

binary_output_data = CKB::Utils.hex_to_bin(output_data).bytes.pack("C*")
hash.update(binary_output_data.bytes.pack("C*"))
end

Digest::SHA256.hexdigest(hash.digest.bytes.pack("C*"))
end

def self.parse_unique_cell(hex_data)
data = hex_data.delete_prefix("0x")
decimal = "0x#{data.slice!(0, 2)}".to_i(16)
Expand Down
1 change: 1 addition & 0 deletions app/views/api/v2/scripts/ckb_transactions.json.jbuilder
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ json.data do
json.display_inputs tx.display_inputs
json.display_outputs tx.display_outputs
json.is_rgb_transaction tx.rgb_transaction?
json.is_btc_time_lock tx.btc_time_transaction?
json.rgb_txid tx.rgb_txid
end
json.meta do
Expand Down
2 changes: 2 additions & 0 deletions config/settings.mainnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,5 @@ proposal_window: 10

# rgbpp code hash
rgbpp_code_hash: "0xbc6c568a1a0d0a09f6844dc9d74ddb4343c32143ff25f727c59edf4fb72d6936"
# btc time code hash
btc_time_code_hash: "0x70d64497a075bd651e98ac030455ea200637ee325a12ad08aff03f1a117e5a62"
3 changes: 3 additions & 0 deletions config/settings.testnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ proposal_window: 10

# rgbpp code hash
rgbpp_code_hash: "0x61ca7a4796a4eb19ca4f0d065cb9b10ddcf002f10f7cbb810c706cb6bb5c3248"
# btc time code hash
btc_time_code_hash: "0x00cdf8fab0f8ac638758ebf5ea5e4052b1d71e8a77b9f43139718621f6849326"

Loading

0 comments on commit fffb89f

Please sign in to comment.