diff --git a/.env.example b/.env.example index 5312bae16..9b1ffa5dc 100644 --- a/.env.example +++ b/.env.example @@ -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. diff --git a/Gemfile b/Gemfile index 4a074b038..f30cf09bc 100644 --- a/Gemfile +++ b/Gemfile @@ -73,6 +73,8 @@ gem "sentry-sidekiq" gem "bigdecimal" +gem "nio4r", "> 2.5.8" + # Deployment gem "rack-attack" @@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index 462cbd5bf..dce1d2ae7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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) @@ -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 @@ -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) @@ -570,7 +541,6 @@ DEPENDENCIES sidekiq simplecov simplecov-cobertura - solargraph spring spring-watcher-listen tzinfo-data diff --git a/README.md b/README.md index 58caded3c..4ef2adf12 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 ``` @@ -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. @@ -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). - diff --git a/app/jobs/import_bitcoin_utxo_job.rb b/app/jobs/import_bitcoin_utxo_job.rb index 7c647b903..44e497481 100644 --- a/app/jobs/import_bitcoin_utxo_job.rb +++ b/app/jobs/import_bitcoin_utxo_job.rb @@ -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, diff --git a/app/models/ckb_sync/api.rb b/app/models/ckb_sync/api.rb index cf57e4cf2..76296964a 100644 --- a/app/models/ckb_sync/api.rb +++ b/app/models/ckb_sync/api.rb @@ -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:) diff --git a/app/models/concerns/ckb_transactions/bitcoin.rb b/app/models/concerns/ckb_transactions/bitcoin.rb index f050e7542..8a44d40d4 100644 --- a/app/models/concerns/ckb_transactions/bitcoin.rb +++ b/app/models/concerns/ckb_transactions/bitcoin.rb @@ -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? @@ -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 diff --git a/app/serializers/ckb_transaction_serializer.rb b/app/serializers/ckb_transaction_serializer.rb index 052f3c066..a28d94d1a 100644 --- a/app/serializers/ckb_transaction_serializer.rb +++ b/app/serializers/ckb_transaction_serializer.rb @@ -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 diff --git a/app/serializers/ckb_transactions_serializer.rb b/app/serializers/ckb_transactions_serializer.rb index 1de498713..5ebc57f85 100644 --- a/app/serializers/ckb_transactions_serializer.rb +++ b/app/serializers/ckb_transactions_serializer.rb @@ -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 diff --git a/app/utils/ckb_utils.rb b/app/utils/ckb_utils.rb index 2d7dda544..fc81393ec 100644 --- a/app/utils/ckb_utils.rb +++ b/app/utils/ckb_utils.rb @@ -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)}" @@ -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. @@ -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) diff --git a/app/views/api/v2/scripts/ckb_transactions.json.jbuilder b/app/views/api/v2/scripts/ckb_transactions.json.jbuilder index cf67200f8..9f90eff7f 100644 --- a/app/views/api/v2/scripts/ckb_transactions.json.jbuilder +++ b/app/views/api/v2/scripts/ckb_transactions.json.jbuilder @@ -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 diff --git a/config/settings.mainnet.yml b/config/settings.mainnet.yml index 8e70c0c15..1130bfd3f 100644 --- a/config/settings.mainnet.yml +++ b/config/settings.mainnet.yml @@ -76,3 +76,5 @@ proposal_window: 10 # rgbpp code hash rgbpp_code_hash: "0xbc6c568a1a0d0a09f6844dc9d74ddb4343c32143ff25f727c59edf4fb72d6936" +# btc time code hash +btc_time_code_hash: "0x70d64497a075bd651e98ac030455ea200637ee325a12ad08aff03f1a117e5a62" diff --git a/config/settings.testnet.yml b/config/settings.testnet.yml index 71b0ea890..251898c99 100644 --- a/config/settings.testnet.yml +++ b/config/settings.testnet.yml @@ -80,3 +80,6 @@ proposal_window: 10 # rgbpp code hash rgbpp_code_hash: "0x61ca7a4796a4eb19ca4f0d065cb9b10ddcf002f10f7cbb810c706cb6bb5c3248" +# btc time code hash +btc_time_code_hash: "0x00cdf8fab0f8ac638758ebf5ea5e4052b1d71e8a77b9f43139718621f6849326" + diff --git a/test/controllers/api/v1/address_dao_transactions_controller_test.rb b/test/controllers/api/v1/address_dao_transactions_controller_test.rb index b2a0143d8..4f45ebcfe 100644 --- a/test/controllers/api/v1/address_dao_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_dao_transactions_controller_test.rb @@ -91,7 +91,7 @@ class AddressDaoTransactionsControllerTest < ActionDispatch::IntegrationTest assert_equal %w(block_number block_timestamp display_inputs display_inputs_count display_outputs display_outputs_count income transaction_hash is_rgb_transaction - is_cellbase created_at create_timestamp rgb_txid).sort, + is_cellbase created_at create_timestamp rgb_txid is_btc_time_lock).sort, response_tx_transaction["attributes"].keys.sort end diff --git a/test/controllers/api/v1/address_pending_transactions_controller_test.rb b/test/controllers/api/v1/address_pending_transactions_controller_test.rb index deeb16941..7150d7862 100644 --- a/test/controllers/api/v1/address_pending_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_pending_transactions_controller_test.rb @@ -139,6 +139,7 @@ class AddressPendingTransactionsControllerTest < ActionDispatch::IntegrationTest is_cellbase transaction_hash is_rgb_transaction + is_btc_time_lock created_at create_timestamp rgb_txid diff --git a/test/controllers/api/v1/address_transactions_controller_test.rb b/test/controllers/api/v1/address_transactions_controller_test.rb index 80e6ad596..b9295530c 100644 --- a/test/controllers/api/v1/address_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_transactions_controller_test.rb @@ -141,6 +141,7 @@ class AddressTransactionsControllerTest < ActionDispatch::IntegrationTest is_cellbase transaction_hash is_rgb_transaction + is_btc_time_lock rgb_txid created_at create_timestamp diff --git a/test/controllers/api/v1/address_udt_transactions_controller_test.rb b/test/controllers/api/v1/address_udt_transactions_controller_test.rb index 113c2dca3..678f1ecbb 100644 --- a/test/controllers/api/v1/address_udt_transactions_controller_test.rb +++ b/test/controllers/api/v1/address_udt_transactions_controller_test.rb @@ -96,7 +96,7 @@ class AddressUdtTransactionsControllerTest < ActionDispatch::IntegrationTest assert_equal %w(block_number block_timestamp display_inputs display_inputs_count display_outputs display_outputs_count income is_cellbase transaction_hash - created_at create_timestamp is_rgb_transaction rgb_txid).sort, response_tx_transaction["attributes"].keys.sort + created_at create_timestamp is_rgb_transaction rgb_txid is_btc_time_lock).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by id" do diff --git a/test/controllers/api/v1/block_transactions_controller_test.rb b/test/controllers/api/v1/block_transactions_controller_test.rb index f49eaee50..4d07cca46 100644 --- a/test/controllers/api/v1/block_transactions_controller_test.rb +++ b/test/controllers/api/v1/block_transactions_controller_test.rb @@ -105,7 +105,7 @@ class BlockTransactionsControllerTest < ActionDispatch::IntegrationTest block_number block_timestamp display_inputs display_inputs_count display_outputs display_outputs_count income is_cellbase transaction_hash is_rgb_transaction - created_at create_timestamp rgb_txid + created_at create_timestamp rgb_txid is_btc_time_lock ).sort, response_tx_transaction["attributes"].keys.sort end diff --git a/test/controllers/api/v1/ckb_transactions_controller_test.rb b/test/controllers/api/v1/ckb_transactions_controller_test.rb index 25310ec8f..c55e466c1 100644 --- a/test/controllers/api/v1/ckb_transactions_controller_test.rb +++ b/test/controllers/api/v1/ckb_transactions_controller_test.rb @@ -7,6 +7,7 @@ class CkbTransactionsControllerTest < ActionDispatch::IntegrationTest block_number transaction_hash block_timestamp transaction_fee bytes version display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps tx_status detailed_message largest_tx largest_tx_in_epoch cycles max_cycles_in_epoch max_cycles is_rgb_transaction rgb_txid + is_btc_time_lock ).sort setup do CkbSync::Api.any_instance.stubs(:get_blockchain_info).returns(OpenStruct.new(chain: "ckb_testnet")) diff --git a/test/controllers/api/v1/contract_transactions_controller_test.rb b/test/controllers/api/v1/contract_transactions_controller_test.rb index 2e2ce8a9a..a8eddd35e 100644 --- a/test/controllers/api/v1/contract_transactions_controller_test.rb +++ b/test/controllers/api/v1/contract_transactions_controller_test.rb @@ -61,7 +61,7 @@ class ContractTransactionsControllerTest < ActionDispatch::IntegrationTest assert_equal %w(block_number block_timestamp display_inputs display_inputs_count display_outputs display_outputs_count income is_cellbase transaction_hash - created_at create_timestamp is_rgb_transaction rgb_txid).sort, response_tx_transaction["attributes"].keys.sort + created_at create_timestamp is_rgb_transaction rgb_txid is_btc_time_lock).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when no records found by give contract name" do diff --git a/test/controllers/api/v1/dao_contract_transactions_controller_test.rb b/test/controllers/api/v1/dao_contract_transactions_controller_test.rb index 47b2ea258..6480fa4bb 100644 --- a/test/controllers/api/v1/dao_contract_transactions_controller_test.rb +++ b/test/controllers/api/v1/dao_contract_transactions_controller_test.rb @@ -128,7 +128,8 @@ class DaoContractTransactionsControllerTest < ActionDispatch::IntegrationTest response_tx_transaction = json["data"] assert_equal %w(block_number transaction_hash block_timestamp transaction_fee version bytes display_inputs display_outputs is_cellbase income witnesses cell_deps header_deps tx_status detailed_message - largest_tx largest_tx_in_epoch cycles max_cycles_in_epoch max_cycles is_rgb_transaction rgb_txid).sort, response_tx_transaction["attributes"].keys.sort + largest_tx largest_tx_in_epoch cycles max_cycles_in_epoch max_cycles is_rgb_transaction rgb_txid + is_btc_time_lock).sort, response_tx_transaction["attributes"].keys.sort end test "should return error object when given tx hash corresponds to a normal transaction" do diff --git a/test/controllers/api/v1/udt_transactions_controller_test.rb b/test/controllers/api/v1/udt_transactions_controller_test.rb index 7ec8f4f23..2548a3fc3 100644 --- a/test/controllers/api/v1/udt_transactions_controller_test.rb +++ b/test/controllers/api/v1/udt_transactions_controller_test.rb @@ -92,7 +92,7 @@ class UdtTransactionsControllerTest < ActionDispatch::IntegrationTest assert_equal %w( block_number block_timestamp display_inputs display_inputs_count created_at create_timestamp display_outputs display_outputs_count income is_cellbase is_rgb_transaction transaction_hash - rgb_txid + rgb_txid is_btc_time_lock ).sort, response_tx_transaction["attributes"].keys.sort end diff --git a/test/utils/ckb_utils_test.rb b/test/utils/ckb_utils_test.rb index dba14ccae..de272aef8 100644 --- a/test/utils/ckb_utils_test.rb +++ b/test/utils/ckb_utils_test.rb @@ -470,6 +470,26 @@ class CkbUtilsTest < ActiveSupport::TestCase assert_nil info[:sybol] end + test "calculate commitment" do + data = JSON.parse('{"inputs":[{"previous_output":{"tx_hash":"0x047b6894a0b7a4d7a73b1503d1ae35c51fc5fa6306776dcf22b1fb3daaa32a29","index":"0x0"},"since":"0x0"}],"outputs":[{"lock":{"code_hash":"0xd5a4e241104041f6f12f11bddcf30bd7b2f818722f78353fde019f5081cd6b49","hash_type":"type","args":"0x010000000000000000000000000000000000000000000000000000000000000000000000"},"capacity":"0x0000000000000000","type":{"code_hash":"0xc4957f239eb3db9f5c5fb949e9dd99adbb8068b8ac7fe7ae49495486d5e5d235","hash_type":"type","args":"0x43094caf2f2bcdf6f5ab02c2de744936897278d558a2b6924db98a4f27d629e2"}},{"lock":{"code_hash":"0xd5a4e241104041f6f12f11bddcf30bd7b2f818722f78353fde019f5081cd6b49","hash_type":"type","args":"0x010000000000000000000000000000000000000000000000000000000000000000000000"},"capacity":"0x0000000000000000","type":{"code_hash":"0xc4957f239eb3db9f5c5fb949e9dd99adbb8068b8ac7fe7ae49495486d5e5d235","hash_type":"type","args":"0x43094caf2f2bcdf6f5ab02c2de744936897278d558a2b6924db98a4f27d629e2"}}],"outputs_data":["0x2c010000000000000000000000000000","0xbc020000000000000000000000000000"],"cell_deps":[]}') + transaction = CKB::Types::Transaction.from_h(data.with_indifferent_access) + CkbTransaction.stubs(:fetch_sdk_transaction).returns(transaction) + + commitment = CkbUtils.calculate_commitment("0x") + assert_equal commitment, "7cdecc8cc293d491a0cbf44e92feabfc29e79408c1d2f7547b334c42efe13131" + end + + test "parse btc time lock args" do + args = "0x7d00000010000000590000005d000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce801140000000ba4ece3bd6d00fc9f3d828a909c3c6384c9c5130600000001e1a7d37d4580db85942b3a3771189635fba2bffd6e65aaa31c3411a8248236" + parsed_args = CkbUtils.parse_btc_time_lock_cell(args) + + assert_equal "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", parsed_args.lock.code_hash + assert_equal "type", parsed_args.lock.hash_type + assert_equal "0x140000000ba4ece3bd6d00fc9f3d828a909c3c6384c9c513", parsed_args.lock.args + assert_equal 6, parsed_args.after + assert_equal "368224a811341ca3aa656efdbfa2fb35961871373a2b9485db80457dd3a7e101", parsed_args.txid + end + private def node_data_processor