From e4a7ddd9b7a52bbff13ba805a48701329f3263c5 Mon Sep 17 00:00:00 2001 From: Rabbit Date: Sat, 20 Apr 2024 16:20:08 +0800 Subject: [PATCH] feat: parse btc time lock cell (#1808) --- .../concerns/ckb_transactions/bitcoin.rb | 12 ++++++-- app/utils/ckb_utils.rb | 28 +++++++++++++++++++ test/utils/ckb_utils_test.rb | 11 ++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/ckb_transactions/bitcoin.rb b/app/models/concerns/ckb_transactions/bitcoin.rb index 1647f7504..8a44d40d4 100644 --- a/app/models/concerns/ckb_transactions/bitcoin.rb +++ b/app/models/concerns/ckb_transactions/bitcoin.rb @@ -26,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/utils/ckb_utils.rb b/app/utils/ckb_utils.rb index 1c2ec8e1b..fc81393ec 100644 --- a/app/utils/ckb_utils.rb +++ b/app/utils/ckb_utils.rb @@ -656,6 +656,34 @@ 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. diff --git a/test/utils/ckb_utils_test.rb b/test/utils/ckb_utils_test.rb index 49246f48e..de272aef8 100644 --- a/test/utils/ckb_utils_test.rb +++ b/test/utils/ckb_utils_test.rb @@ -479,6 +479,17 @@ class CkbUtilsTest < ActiveSupport::TestCase 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