block_io 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/examples/sweeper.rb +24 -0
- data/lib/block_io.rb +100 -3
- data/lib/block_io/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d0ac0a444bdd6e778287026a7d9e89ef91b25ddd
|
|
4
|
+
data.tar.gz: 7e9bb72332639245c289c04f474be22ea512eba9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d06356ff689a5e4a8f30787d1683c2277b4237120daad993e4dac9b564c975d284427c8a147fe61194c1cef06cfb663cfff91bc26c0f86a8a42f3ee3aa58fa86
|
|
7
|
+
data.tar.gz: 36554afd1641aed6eee40442178a16d337a6174a263f559eefdaf7e63915edbe56a726d738bb8feaa4c69851b984e6b4106149070a13de1a81995de7be175118
|
data/README.md
CHANGED
|
@@ -14,10 +14,12 @@ And then execute:
|
|
|
14
14
|
|
|
15
15
|
Or install it yourself as:
|
|
16
16
|
|
|
17
|
-
$ gem install block_io -v=1.0.
|
|
17
|
+
$ gem install block_io -v=1.0.6
|
|
18
18
|
|
|
19
19
|
## Changelog
|
|
20
20
|
|
|
21
|
+
*01/21/15*: Added ability to sweep coins from one address to another.
|
|
22
|
+
*11/04/14*: Fix issue with nil parameters in an API call.
|
|
21
23
|
*11/03/14*: Reduce dependence on OpenSSL. PBKDF2 function is now Ruby-based. Should work well with Heroku's libraries.
|
|
22
24
|
*10/18/14*: Now using deterministic signatures (RFC6979), and BIP62 to hinder transaction malleability.
|
|
23
25
|
|
data/examples/sweeper.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Example script for sweeping all coins from a given address
|
|
2
|
+
# Must use the API Key for the Network the address belongs to
|
|
3
|
+
# Must also provide the Private Key to the sweep address in Wallet Import Format (WIF)
|
|
4
|
+
#
|
|
5
|
+
# Contact support@block.io if you have any issues
|
|
6
|
+
|
|
7
|
+
require "block_io"
|
|
8
|
+
|
|
9
|
+
BlockIo.set_options :api_key => 'YOUR API KEY', :pin => 'PIN NOT NEEDED', :version => 2
|
|
10
|
+
|
|
11
|
+
to_address = 'SWEEP COINS TO THIS ADDRESS'
|
|
12
|
+
|
|
13
|
+
from_address = 'SWEEP COINS FROM THIS ADDRESS'
|
|
14
|
+
private_key = 'PRIVATE KEY FOR FROM_ADDRESS'
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
response = BlockIo.sweep_from_address(:to_address => to_address, :private_key => private_key, :from_address => from_address)
|
|
18
|
+
|
|
19
|
+
puts "Sweep Complete: #{response['data']['amount_sent']} #{response['data']['network']} swept from #{from_address} to #{to_address}."
|
|
20
|
+
puts "Transaction ID: #{response['data']['txid']}"
|
|
21
|
+
puts "Network Fee Incurred: #{response['data']['network_fee']} #{response['data']['network']}"
|
|
22
|
+
rescue Exception => e
|
|
23
|
+
puts "Sweep failed: #{e}"
|
|
24
|
+
end
|
data/lib/block_io.rb
CHANGED
|
@@ -36,9 +36,12 @@ module BlockIo
|
|
|
36
36
|
method_name = m.to_s
|
|
37
37
|
|
|
38
38
|
if ['withdraw', 'withdraw_from_address', 'withdraw_from_addresses', 'withdraw_from_user', 'withdraw_from_users', 'withdraw_from_label', 'withdraw_from_labels'].include?(m.to_s) then
|
|
39
|
-
|
|
39
|
+
# need to withdraw from an address
|
|
40
40
|
self.withdraw(args.first, m.to_s)
|
|
41
41
|
|
|
42
|
+
elsif ['sweep_from_address'].include?(m.to_s) then
|
|
43
|
+
# need to sweep from an address
|
|
44
|
+
self.sweep(args.first, m.to_s)
|
|
42
45
|
else
|
|
43
46
|
params = get_params(args.first)
|
|
44
47
|
self.api_call([method_name, params])
|
|
@@ -96,6 +99,51 @@ module BlockIo
|
|
|
96
99
|
|
|
97
100
|
end
|
|
98
101
|
|
|
102
|
+
def self.sweep(args = {}, method_name = 'sweep_from_address')
|
|
103
|
+
# sweep coins from a given address + key
|
|
104
|
+
|
|
105
|
+
raise Exception.new("No private_key provided.") unless args.has_key?(:private_key)
|
|
106
|
+
|
|
107
|
+
key = Key.from_wif(args[:private_key])
|
|
108
|
+
|
|
109
|
+
args[:public_key] = key.public_key # so Block.io can match things up
|
|
110
|
+
args.delete(:private_key) # the key must never leave this machine
|
|
111
|
+
|
|
112
|
+
params = get_params(args)
|
|
113
|
+
|
|
114
|
+
response = self.api_call([method_name, params])
|
|
115
|
+
|
|
116
|
+
if response['data'].has_key?('reference_id') then
|
|
117
|
+
# Block.io's asking us to provide some client-side signatures, let's get to it
|
|
118
|
+
|
|
119
|
+
# let's sign all the inputs we can
|
|
120
|
+
inputs = response['data']['inputs']
|
|
121
|
+
|
|
122
|
+
inputs.each do |input|
|
|
123
|
+
# iterate over all signers
|
|
124
|
+
|
|
125
|
+
input['signers'].each do |signer|
|
|
126
|
+
# if our public key matches this signer's public key, sign the data
|
|
127
|
+
|
|
128
|
+
signer['signed_data'] = key.sign(input['data_to_sign']) if signer['signer_public_key'] == key.public_key
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# the response object is now signed, let's stringify it and finalize this withdrawal
|
|
135
|
+
|
|
136
|
+
response = self.api_call(['sign_and_finalize_sweep',{:signature_data => response['data'].to_json}])
|
|
137
|
+
|
|
138
|
+
# if we provided all the required signatures, this transaction went through
|
|
139
|
+
# otherwise Block.io responded with data asking for more signatures
|
|
140
|
+
# the latter will be the case for dTrust addresses
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
return response
|
|
144
|
+
|
|
145
|
+
end
|
|
146
|
+
|
|
99
147
|
|
|
100
148
|
private
|
|
101
149
|
|
|
@@ -139,12 +187,13 @@ module BlockIo
|
|
|
139
187
|
|
|
140
188
|
class Key
|
|
141
189
|
|
|
142
|
-
def initialize(privkey = nil)
|
|
190
|
+
def initialize(privkey = nil, compressed = true)
|
|
143
191
|
# the privkey must be in hex if at all provided
|
|
144
192
|
|
|
145
193
|
@group = ECDSA::Group::Secp256k1
|
|
146
194
|
@private_key = privkey.to_i(16) || 1 + SecureRandom.random_number(group.order - 1)
|
|
147
195
|
@public_key = @group.generator.multiply_by_scalar(@private_key)
|
|
196
|
+
@compressed = compressed
|
|
148
197
|
|
|
149
198
|
end
|
|
150
199
|
|
|
@@ -156,7 +205,7 @@ module BlockIo
|
|
|
156
205
|
def public_key
|
|
157
206
|
# returns the compressed form of the public key to save network fees (shorter scripts)
|
|
158
207
|
|
|
159
|
-
return ECDSA::Format::PointOctetString.encode(@public_key, compression:
|
|
208
|
+
return ECDSA::Format::PointOctetString.encode(@public_key, compression: @compressed).unpack("H*")[0]
|
|
160
209
|
end
|
|
161
210
|
|
|
162
211
|
def sign(data)
|
|
@@ -189,6 +238,19 @@ module BlockIo
|
|
|
189
238
|
|
|
190
239
|
return Key.new(hashed_key)
|
|
191
240
|
end
|
|
241
|
+
|
|
242
|
+
def self.from_wif(wif)
|
|
243
|
+
# returns a new key extracted from the Wallet Import Format provided
|
|
244
|
+
# TODO check against checksum
|
|
245
|
+
|
|
246
|
+
hexkey = Helper.decode_base58(wif)
|
|
247
|
+
actual_key = hexkey[2...66]
|
|
248
|
+
|
|
249
|
+
compressed = hexkey[2..hexkey.length].length-8 > 64 and hexkey[2..hexkey.length][64...66] == '01'
|
|
250
|
+
|
|
251
|
+
return Key.new(actual_key, compressed)
|
|
252
|
+
|
|
253
|
+
end
|
|
192
254
|
|
|
193
255
|
def isPositive(i)
|
|
194
256
|
sig = "!+-"[i <=> 0]
|
|
@@ -304,6 +366,41 @@ module BlockIo
|
|
|
304
366
|
aes.iv = iv if iv != nil
|
|
305
367
|
Base64.strict_encode64(aes.update(data) + aes.final)
|
|
306
368
|
end
|
|
369
|
+
|
|
370
|
+
# courtesy bitcoin-ruby
|
|
371
|
+
|
|
372
|
+
def self.int_to_base58(int_val, leading_zero_bytes=0)
|
|
373
|
+
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
374
|
+
base58_val, base = '', alpha.size
|
|
375
|
+
while int_val > 0
|
|
376
|
+
int_val, remainder = int_val.divmod(base)
|
|
377
|
+
base58_val = alpha[remainder] + base58_val
|
|
378
|
+
end
|
|
379
|
+
base58_val
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def self.base58_to_int(base58_val)
|
|
383
|
+
alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
384
|
+
int_val, base = 0, alpha.size
|
|
385
|
+
base58_val.reverse.each_char.with_index do |char,index|
|
|
386
|
+
raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
|
|
387
|
+
int_val += char_index*(base**index)
|
|
388
|
+
end
|
|
389
|
+
int_val
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def self.encode_base58(hex)
|
|
393
|
+
leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
|
|
394
|
+
("1"*leading_zero_bytes) + Helper.int_to_base58( hex.to_i(16) )
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def self.decode_base58(base58_val)
|
|
398
|
+
s = Helper.base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
|
|
399
|
+
s = '' if s == '00'
|
|
400
|
+
leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
|
|
401
|
+
s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
|
|
402
|
+
s
|
|
403
|
+
end
|
|
307
404
|
end
|
|
308
405
|
|
|
309
406
|
end
|
data/lib/block_io/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: block_io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Atif Nazir
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-01-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- block_io.gemspec
|
|
156
156
|
- examples/basic.rb
|
|
157
157
|
- examples/dtrust.rb
|
|
158
|
+
- examples/sweeper.rb
|
|
158
159
|
- lib/block_io.rb
|
|
159
160
|
- lib/block_io/version.rb
|
|
160
161
|
homepage: https://block.io/api/simple/ruby
|