diff --git a/README.md b/README.md index 761d978f..5d257d41 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,19 @@ tx_hash = bob.send_capacity(alice.address, 1000 * 10**8) api.get_transaction(tx_hash) ``` +Provide wallet a public key + +```ruby +api = CKB::API.new + +bob = CKB::Wallet.new(api, "0x024a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01") +alice = CKB::Wallet.new(api, "0x0257623ec521657a27204c5590384cd59d9267c06d75ab308070be692251b67c57") + +bob_key = "0xe79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3" + +tx_hash = bob.send_capacity(alice.address, 1000 * 10**8, key: bob_key) +``` + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. diff --git a/lib/ckb/wallet.rb b/lib/ckb/wallet.rb index dfa7066d..15f0183f 100644 --- a/lib/ckb/wallet.rb +++ b/lib/ckb/wallet.rb @@ -13,14 +13,24 @@ module CKB class Wallet attr_reader :api - # privkey is a bin string attr_reader :key + attr_reader :pubkey + attr_reader :addr + attr_reader :address # @param api [CKB::API] - # @param key [CKB::Key] + # @param key [CKB::Key | String] Key or pubkey def initialize(api, key) @api = api - @key = key + if key.is_a?(CKB::Key) + @key = key + @pubkey = @key.pubkey + else + @pubkey = key + @key = nil + end + @addr = Address.from_pubkey(@pubkey) + @address = @addr.to_s end def self.from_hex(api, privkey) @@ -45,12 +55,18 @@ def get_balance get_unspent_cells.map { |cell| cell.capacity.to_i }.reduce(0, &:+) end - def generate_tx(target_address, capacity, data = "0x") + # @param target_address [String] + # @param capacity [Integer] + # @param data [String ] "0x..." + # @param key [CKB::Key | String] Key or private key hex string + def generate_tx(target_address, capacity, data = "0x", key: nil) + key = get_key(key) + output = Types::Output.new( capacity: capacity, data: data, lock: Types::Script.generate_lock( - key.address.parse(target_address), + addr.parse(target_address), api.system_script_code_hash ) ) @@ -86,15 +102,22 @@ def generate_tx(target_address, capacity, data = "0x") # @param target_address [String] # @param capacity [Integer] # @param data [String] "0x..." - def send_capacity(target_address, capacity, data = "0x") - tx = generate_tx(target_address, capacity, data) + # @param key [CKB::Key | String] Key or private key hex string + def send_capacity(target_address, capacity, data = "0x", key: nil) + tx = generate_tx(target_address, capacity, data, key: key) send_transaction(tx) end - def deposit_to_dao(capacity) + # @param capacity [Integer] + # @param key [CKB::Key | String] Key or private key hex string + # + # @return [CKB::Type::OutPoint] + def deposit_to_dao(capacity, key: nil) + key = get_key(key) + output = Types::Output.new( capacity: capacity, - lock: Types::Script.generate_lock(@key.address.blake160, DAO_CODE_HASH) + lock: Types::Script.generate_lock(addr.blake160, DAO_CODE_HASH) ) charge_output = Types::Output.new( @@ -126,7 +149,13 @@ def deposit_to_dao(capacity) Types::OutPoint.new(cell: Types::CellOutPoint.new(tx_hash: tx_hash, index: 0)) end - def generate_withdraw_from_dao_transaction(cell_out_point) + # @param cell_out_point [CKB::Type::OutPoint] + # @param key [CKB::Key | String] Key or private key hex string + # + # @return [CKB::Type::Transaction] + def generate_withdraw_from_dao_transaction(cell_out_point, key: nil) + key = get_key(key) + cell_status = api.get_live_cell(cell_out_point) unless cell_status.status == "live" raise "Cell is not yet live!" @@ -188,10 +217,6 @@ def block_assembler_config ).strip end - def address - @key.address.to_s - end - private # @param transaction [CKB::Transaction] @@ -225,10 +250,6 @@ def gather_inputs(capacity, min_capacity, min_charge_capacity) OpenStruct.new(inputs: inputs, capacities: input_capacities, witnesses: witnesses) end - def pubkey - @key.pubkey - end - def lock_hash @lock_hash ||= lock.to_hash end @@ -236,9 +257,34 @@ def lock_hash # @return [CKB::Types::Script] def lock Types::Script.generate_lock( - @key.address.blake160, + addr.blake160, api.system_script_code_hash ) end + + # @param [CKB::Key | String | nil] + # + # @return [CKB::Key] + def get_key(key) + raise "Must provide a private key" unless @key || key + + return @key if @key + + the_key = convert_key(key) + raise "Key not match pubkey" unless the_key.pubkey == @pubkey + + the_key + end + + # @param key [CKB::Key | String] a Key or private key hex string + # + # @return [CKB::Key] + def convert_key(key) + return if key.nil? + + return key if key.is_a?(CKB::Key) + + CKB::Key.new(key) + end end end diff --git a/spec/ckb/wallet_spec.rb b/spec/ckb/wallet_spec.rb new file mode 100644 index 00000000..e12e6617 --- /dev/null +++ b/spec/ckb/wallet_spec.rb @@ -0,0 +1,85 @@ +RSpec.describe CKB::Wallet do + let(:privkey) { "0xe79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3" } + let(:key) { CKB::Key.new(privkey) } + let(:pubkey) { key.pubkey } + let(:api) { nil } + + context "initialize" do + it "Key" do + wallet = CKB::Wallet.new(api, key) + + expect(wallet.key.privkey).to eq key.privkey + expect(wallet.pubkey).to eq key.pubkey + expect(wallet.address).to eq key.address.to_s + end + + it "pubkey" do + wallet = CKB::Wallet.new(api, pubkey) + + expect(wallet.key).to be nil + expect(wallet.pubkey).to eq pubkey + expect(wallet.address).to eq key.address.to_s + end + end + + it "self.from_hex" do + wallet = CKB::Wallet.from_hex(api, privkey) + + expect(wallet.key.privkey).to eq privkey + end + + context "convert_key" do + let(:wallet) { CKB::Wallet.new(api, pubkey) } + it "nil" do + expect( + wallet.send(:convert_key, nil) + ).to be nil + end + + it "Key" do + expect( + wallet.send(:convert_key, key) + ).to be key + end + + it "privkey" do + expect( + wallet.send(:convert_key, privkey) + ).to be_a(CKB::Key) + end + end + + context "get_key" do + context "key exists" do + let(:wallet) { CKB::Wallet.new(api, key) } + + it "return @key" do + the_key = wallet.send(:get_key, key) + + expect(the_key).to be wallet.key + end + end + + context "key not exists" do + let(:wallet) { CKB::Wallet.new(api, pubkey) } + + it "nil" do + expect { + wallet.send(:get_key, nil) + }.to raise_error(RuntimeError) + end + + it "Key" do + expect( + wallet.send(:get_key, key) + ).to eq key + end + + it "privkey" do + expect( + wallet.send(:get_key, privkey) + ).to be_a(CKB::Key) + end + end + end +end \ No newline at end of file