Skip to content

Commit

Permalink
feat: Upgrade SDK with latest CKB changes
Browse files Browse the repository at this point in the history
This commit captures 2 changes:

* It updates the SDK following latest Script refactoring
* It as a new mode in API to automatically load correct lock script in
  case testnet spec is used
  • Loading branch information
xxuejie committed Mar 29, 2019
1 parent 962136f commit ae5c1c5
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
21 changes: 18 additions & 3 deletions lib/ckb/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,23 @@ class API

DEFAULT_URL = "http://localhost:8114"

def initialize(host: DEFAULT_URL)
MODE_TESTNET = "testnet"
MODE_CUSTOM = "custom"

def initialize(host: DEFAULT_URL, mode: MODE_TESTNET)
@uri = URI(host)
if mode == MODE_TESTNET
# For testnet chain, we can assume the first cell of the first transaction
# in the genesis block contains default lock script we can use here.
system_cell_transaction = genesis_block[:commit_transactions][0]
out_point = {
hash: system_cell_transaction[:hash],
index: 0
}
cell_data = CKB::Utils.hex_to_bin(system_cell_transaction[:outputs][0][:data])
cell_hash = CKB::Utils.bin_to_prefix_hex(CKB::Blake2b.digest(cell_data))
self.set_system_script_cell(out_point, cell_hash)
end
end

# @param out_point [Hash] { hash: "0x...", index: 0 }
Expand Down Expand Up @@ -56,8 +71,8 @@ def get_tip_block_number
rpc_request("get_tip_block_number")
end

def get_cells_by_type_hash(hash, from, to)
rpc_request("get_cells_by_type_hash", params: [hash, from, to])
def get_cells_by_lock_hash(hash, from, to)
rpc_request("get_cells_by_lock_hash", params: [hash, from, to])
end

def get_transaction(tx_hash)
Expand Down
30 changes: 9 additions & 21 deletions lib/ckb/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ def self.extract_pubkey_bin(privkey_bin)

def self.json_script_to_type_hash(script)
blake2b = CKB::Blake2b.new
blake2b << hex_to_bin(script[:reference]) if script[:reference]
blake2b << "|"
blake2b << script[:binary] if script[:binary]
signed_args = script[:signed_args] || []
signed_args.each do |arg|
blake2b << hex_to_bin(script[:binary_hash]) if script[:binary_hash]
args = script[:args] || []
args.each do |arg|
blake2b << arg
end
bin_to_prefix_hex(blake2b.digest)
Expand All @@ -41,15 +39,10 @@ def self.sign_sighash_all_inputs(inputs, outputs, privkey)
previous_output = input[:previous_output]
blake2b.update(hex_to_bin(previous_output[:hash]))
blake2b.update(previous_output[:index].to_s)
blake2b.update(
hex_to_bin(
json_script_to_type_hash(input[:unlock])
)
)
end
outputs.each do |output|
blake2b.update(output[:capacity].to_s)
blake2b.update(hex_to_bin(output[:lock]))
blake2b.update(hex_to_bin(json_script_to_type_hash(output[:lock])))
next unless output[:type]

blake2b.update(
Expand All @@ -65,8 +58,8 @@ def self.sign_sighash_all_inputs(inputs, outputs, privkey)
signature_hex = bin_to_hex(signature_bin)

inputs.map do |input|
unlock = input[:unlock].merge(args: [signature_hex, sighash_type])
input.merge(unlock: unlock)
args = input[:args] + [signature_hex, sighash_type]
input.merge(args: args)
end
end

Expand All @@ -76,22 +69,17 @@ def self.sign_sighash_all_inputs(inputs, outputs, privkey)
# hence we have to do type conversions here.
def self.normalize_tx_for_json!(transaction)
transaction[:inputs].each do |input|
unlock = input[:unlock]
unlock[:args] = unlock[:args].map { |arg| bin_to_prefix_hex(arg) }
unlock[:signed_args] = unlock[:signed_args].map { |arg| bin_to_prefix_hex(arg) }
next unless unlock[:binary]

unlock[:binary] = bin_to_prefix_hex(unlock[:binary])
input[:args] = input[:args].map { |arg| bin_to_prefix_hex(arg) }
end

transaction[:outputs].each do |output|
output[:data] = bin_to_prefix_hex(output[:data])
lock = output[:lock]
lock[:args] = lock[:args].map { |arg| bin_to_prefix_hex(arg) }
next unless output[:type]

type = output[:type]
type[:args] = type[:args].map { |arg| bin_to_prefix_hex(arg) }
type[:signed_args] = type[:signed_args].map { |arg| bin_to_prefix_hex(arg) }
type[:binary] = bin_to_prefix_hex(type[:binary]) if type[:binary]
end

transaction
Expand Down
57 changes: 31 additions & 26 deletions lib/ckb/wallet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,13 @@ def self.from_hex(api, privkey_hex)
new(api, CKB::Utils.hex_to_bin(privkey_hex))
end

def address
verify_type_hash
end

def get_unspent_cells
to = api.get_tip_block_number
results = []
current_from = 1
while current_from <= to
current_to = [current_from + 100, to].min
cells = api.get_cells_by_type_hash(address, current_from, current_to)
cells = api.get_cells_by_lock_hash(lock_hash, current_from, current_to)
results.concat(cells)
current_from = current_to + 1
end
Expand All @@ -50,22 +46,22 @@ def get_balance
get_unspent_cells.map { |cell| cell[:capacity] }.reduce(0, &:+)
end

def generate_tx(target_address, capacity)
def generate_tx(target_lock, capacity)
i = gather_inputs(capacity, MIN_CELL_CAPACITY)
input_capacities = i.capacities

outputs = [
{
capacity: capacity,
data: "",
lock: target_address
lock: target_lock
}
]
if input_capacities > capacity
outputs << {
capacity: input_capacities - capacity,
data: "",
lock: address
lock: lock
}
end
{
Expand All @@ -76,10 +72,10 @@ def generate_tx(target_address, capacity)
}
end

# @param target_address [String] "0x..."
# @param target_lock [Hash]
# @param capacity [Integer]
def send_capacity(target_address, capacity)
tx = generate_tx(target_address, capacity)
def send_capacity(target_lock, capacity)
tx = generate_tx(target_lock, capacity)
send_transaction_bin(tx)
end

Expand All @@ -88,6 +84,27 @@ def get_transaction(hash_hex)
api.get_transaction(hash_hex)
end

def lock
@lock ||= {
version: 0,
binary_hash: api.system_script_cell_hash,
args: [
CKB::Utils.bin_to_hex(CKB::Blake2b.digest(CKB::Blake2b.digest(pubkey_bin)))
]
}
end

def block_assembler_config
args = lock[:args].map do |arg|
"[#{arg.bytes.map(&:to_s).join(", ")}]"
end.join(", ")
%Q(
[block_assembler]
binary_hash = "#{lock[:binary_hash]}"
args = [#{args}]
).strip
end

private

def send_transaction_bin(transaction)
Expand All @@ -103,7 +120,7 @@ def gather_inputs(capacity, min_capacity)
get_unspent_cells.each do |cell|
input = {
previous_output: cell[:out_point],
unlock: verify_script_json_object
args: [pubkey]
}
inputs << input
input_capacities += cell[:capacity]
Expand All @@ -124,20 +141,8 @@ def pubkey_bin
CKB::Utils.extract_pubkey_bin(privkey)
end

def verify_script_json_object
{
version: 0,
reference: api.system_script_cell_hash,
signed_args: [
# We could of course just hash raw bytes, but since right now CKB
# CLI already uses this scheme, we stick to the same way for compatibility
pubkey
]
}
end

def verify_type_hash
@verify_type_hash ||= CKB::Utils.json_script_to_type_hash(verify_script_json_object)
def lock_hash
@lock_hash ||= CKB::Utils.json_script_to_type_hash(lock)
end
end
end
Expand Down

0 comments on commit ae5c1c5

Please sign in to comment.