block_io 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/{.appveyor.yml → .appveyor.yml-disabled} +2 -2
  3. data/.gitignore +1 -0
  4. data/.travis.yml +1 -1
  5. data/LICENSE +1 -1
  6. data/README.md +14 -13
  7. data/block_io.gemspec +6 -6
  8. data/examples/basic.rb +29 -5
  9. data/examples/dtrust.rb +43 -24
  10. data/examples/sweeper.rb +21 -16
  11. data/lib/block_io.rb +3 -2
  12. data/lib/block_io/api_exception.rb +11 -0
  13. data/lib/block_io/chainparams/BTC.yml +8 -0
  14. data/lib/block_io/chainparams/BTCTEST.yml +8 -0
  15. data/lib/block_io/chainparams/DOGE.yml +8 -0
  16. data/lib/block_io/chainparams/DOGETEST.yml +8 -0
  17. data/lib/block_io/chainparams/LTC.yml +8 -0
  18. data/lib/block_io/chainparams/LTCTEST.yml +8 -0
  19. data/lib/block_io/client.rb +143 -79
  20. data/lib/block_io/extended_bitcoinrb.rb +127 -0
  21. data/lib/block_io/helper.rb +137 -39
  22. data/lib/block_io/key.rb +11 -124
  23. data/lib/block_io/version.rb +1 -1
  24. data/spec/client_misc_spec.rb +76 -0
  25. data/spec/client_spec.rb +23 -178
  26. data/spec/dtrust_spec.rb +167 -0
  27. data/spec/helper_spec.rb +1 -1
  28. data/spec/key_spec.rb +50 -19
  29. data/spec/larger_transaction_spec.rb +351 -0
  30. data/spec/sweep_spec.rb +115 -0
  31. data/spec/test-cases/.gitignore +2 -0
  32. data/spec/test-cases/LICENSE +21 -0
  33. data/spec/test-cases/README.md +2 -0
  34. data/spec/test-cases/json/create_and_sign_transaction_response.json +61 -0
  35. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json +1261 -0
  36. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json +1266 -0
  37. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json +1271 -0
  38. data/spec/test-cases/json/create_and_sign_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json +3816 -0
  39. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2SH_3of5_195inputs.json +2931 -0
  40. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2SH_4of5_195inputs.json +5 -0
  41. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_251inputs.json +3771 -0
  42. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_252inputs.json +3786 -0
  43. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_3of5_253inputs.json +3801 -0
  44. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_251inputs.json +5 -0
  45. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_252inputs.json +5 -0
  46. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_P2WSH-over-P2SH_4of5_253inputs.json +5 -0
  47. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_251inputs.json +3771 -0
  48. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_252inputs.json +3786 -0
  49. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_3of5_253inputs.json +3801 -0
  50. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_251inputs.json +5 -0
  51. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_252inputs.json +5 -0
  52. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_WITNESS_V0_4of5_253inputs.json +5 -0
  53. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json +21 -0
  54. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2sh_4_of_5_keys.json +5 -0
  55. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json +21 -0
  56. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_4_of_5_keys.json +5 -0
  57. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json +36 -0
  58. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_251outputs.json +591 -0
  59. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_252outputs.json +576 -0
  60. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3of5_253outputs.json +531 -0
  61. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4_of_5_keys.json +5 -0
  62. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_251outputs.json +5 -0
  63. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_252outputs.json +5 -0
  64. data/spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_4of5_253outputs.json +5 -0
  65. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2pkh.json +5 -0
  66. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh.json +5 -0
  67. data/spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json +5 -0
  68. data/spec/test-cases/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +21 -0
  69. data/spec/test-cases/json/get_balance_response.json +8 -0
  70. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2SH_3of5_195inputs.json +1397 -0
  71. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2SH_4of5_195inputs.json +1397 -0
  72. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_251inputs.json +1795 -0
  73. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_252inputs.json +1802 -0
  74. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_3of5_253inputs.json +1809 -0
  75. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_251inputs.json +1789 -0
  76. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_252inputs.json +1802 -0
  77. data/spec/test-cases/json/prepare_dtrust_transaction_response_P2WSH-over-P2SH_4of5_253inputs.json +1809 -0
  78. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_251inputs.json +1795 -0
  79. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_252inputs.json +1802 -0
  80. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_3of5_253inputs.json +1809 -0
  81. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_251inputs.json +1795 -0
  82. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_252inputs.json +1802 -0
  83. data/spec/test-cases/json/prepare_dtrust_transaction_response_WITNESS_V0_4of5_253inputs.json +1809 -0
  84. data/spec/test-cases/json/prepare_dtrust_transaction_response_p2sh.json +45 -0
  85. data/spec/test-cases/json/prepare_dtrust_transaction_response_p2wsh_over_p2sh.json +45 -0
  86. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0.json +52 -0
  87. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_251outputs.json +1805 -0
  88. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_252outputs.json +1804 -0
  89. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_3of5_253outputs.json +1789 -0
  90. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_251outputs.json +1805 -0
  91. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_252outputs.json +1804 -0
  92. data/spec/test-cases/json/prepare_dtrust_transaction_response_witness_v0_4of5_253outputs.json +1789 -0
  93. data/spec/test-cases/json/prepare_sweep_transaction_response_p2pkh.json +35 -0
  94. data/spec/test-cases/json/prepare_sweep_transaction_response_p2wpkh.json +35 -0
  95. data/spec/test-cases/json/prepare_sweep_transaction_response_p2wpkh_over_p2sh.json +35 -0
  96. data/spec/test-cases/json/prepare_transaction_response.json +164 -0
  97. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_251inputs.json +1796 -0
  98. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_252inputs.json +1803 -0
  99. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_253inputs.json +1810 -0
  100. data/spec/test-cases/json/prepare_transaction_response_P2WSH-over-P2SH_1of2_762inputs.json +5367 -0
  101. data/spec/test-cases/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +76 -0
  102. data/spec/test-cases/json/summarize_prepared_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json +6 -0
  103. metadata +187 -45
  104. data/examples/max_withdrawal.rb +0 -29
  105. data/lib/block_io/constants.rb +0 -10
  106. data/spec/data/sign_and_finalize_dtrust_withdrawal_request.json +0 -1
  107. data/spec/data/sign_and_finalize_sweep_request.json +0 -1
  108. data/spec/data/sign_and_finalize_withdrawal_request.json +0 -4
  109. data/spec/data/sweep_from_address_response.json +0 -1
  110. data/spec/data/withdraw_from_dtrust_address_response.json +0 -1
  111. data/spec/data/withdraw_response.json +0 -1227
  112. data/spec/rfc6979_spec.rb +0 -59
  113. data/spec/withdraw_spec.rb +0 -90
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb3eee8a4c13ad41febe0e5f07271c0e9d8cab44cfc5540d898aa8802c89e334
4
- data.tar.gz: 52c0524074d9ea5da705be0190b613c56e10b1f52f5bdd10bbac163a415ba2e5
3
+ metadata.gz: c7a878068a7ce224442ab3544657e2fb5de30dc68c43b00fd443c7c8ea35a61a
4
+ data.tar.gz: '09ad2302125ca6d86427637023439ffac4f8c1e7d6f477d098aeb6955d0f3be2'
5
5
  SHA512:
6
- metadata.gz: 8b85df794d052257b15add0e7a298b6b3482f04eaa67db0d4fe8b944898fd8a9e00451a2f4f6b351f867a8e33db7694421b2a419102941e41b9543a0a5ba2048
7
- data.tar.gz: b58bb5628274ac2609a6103e68ba0fe5b829705231ee4264126521703907b83e46da97b6ebc9a8698c5d3ff39c11685011fcd4aa6e3d287fdb5ead9e0553054d
6
+ metadata.gz: bb922e78d5c23c2e64b8259a688189eab1c20e654fa63389cc919d28a68030a0650101df2a32aecafbabb6ff639552e2698def07cd405638f0aea6bed32e7995
7
+ data.tar.gz: 940ff69d78033e939b150af65fc89944a8fd99a4296bab9c8314b5988508e2d093ec1e6c1295df37ebcbac4d31110fc70e3749d02a58da25585039594b872de6
@@ -4,8 +4,6 @@ skip_tags: true
4
4
 
5
5
  environment:
6
6
  matrix:
7
- - ruby_version: "23"
8
- - ruby_version: "23-x64"
9
7
  - ruby_version: "24"
10
8
  - ruby_version: "24-x64"
11
9
  - ruby_version: "25"
@@ -14,6 +12,8 @@ environment:
14
12
  - ruby_version: "26-x64"
15
13
  - ruby_version: "27"
16
14
  - ruby_version: "27-x64"
15
+ - ruby_version: "30"
16
+ - ruby_version: "30-x64"
17
17
 
18
18
  install:
19
19
  - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
data/.gitignore CHANGED
@@ -3,3 +3,4 @@ vendor/
3
3
  .bundle/
4
4
  *.gem
5
5
  *~
6
+ .DS_Store
data/.travis.yml CHANGED
@@ -2,11 +2,11 @@ language: ruby
2
2
  os:
3
3
  - linux
4
4
  rvm:
5
- - 2.3
6
5
  - 2.4
7
6
  - 2.5
8
7
  - 2.6
9
8
  - 2.7
9
+ - 3.0
10
10
  bundler_args: --jobs=2
11
11
  script:
12
12
  - bundle exec rspec
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014 BlockIo
3
+ Copyright (c) 2021 BlockIo
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # BlockIo
2
2
 
3
- This Ruby Gem is the official reference client for the Block.io payments API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from <a href="https://block.io" target="_blank">Block.io</a>. Go ahead, sign up :)
3
+ This Ruby Gem is the official reference client for the Block.io's infrastructure APIs. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from <a href="https://block.io" target="_blank">Block.io</a>. Go ahead, sign up :)
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,29 +14,30 @@ And then execute:
14
14
 
15
15
  Or install it yourself as:
16
16
 
17
- $ gem install block_io -v=2.0.0
17
+ $ gem install block_io -v=3.0.0
18
18
 
19
19
  ## Changelog
20
+ *04/14/21*: BREAKING CHANGES. Version 3.0.0. Remove support for Ruby < 2.4.0, and Windows. Behavior and interfaces have changed. By upgrading you'll need to revise your code and tests.
20
21
 
21
- *07/02/20*: BREAKING CHANGES. Version 2.0.0. Remove support for Ruby < 2.3.0. Behavior and interfaces have changed. By upgrading you'll need to revise your code and tests.
22
- *05/10/19*: Prevent inadvertent passing of PINs (user error).
23
- *06/25/18*: Remove support for Ruby < 1.9.3 (OpenSSL::Cipher::Cipher). Remove connection_pool dependency.
24
- *01/21/15*: Added ability to sweep coins from one address to another.
25
- *11/04/14*: Fix issue with nil parameters in an API call.
26
- *11/03/14*: Reduce dependence on OpenSSL. PBKDF2 function is now Ruby-based. Should work well with Heroku's libraries.
27
- *10/18/14*: Now using deterministic signatures (RFC6979), and BIP62 to hinder transaction malleability.
28
-
22
+ ## Important Notes
23
+ * This gem depends on the bitcoinrb gem. By using this gem, your application will load the bitcoinrb gem as well with the Bitcoin namespace. It may conflict with another gem using the same namespace.
24
+ * Transaction endpoints are updated as of v3.0.0.
25
+ * See the examples/ folder for basic examples.
26
+ * Be careful to test thoroughly before production.
27
+ * Use of this software is subject to its LICENSE.
29
28
 
30
29
  ## Usage
31
30
 
32
31
  It's super easy to get started. In your Ruby shell ($ irb), for example, do this:
33
32
 
34
33
  require 'block_io'
35
- blockio = BlockIo::Client.new(:api_key => "API KEY", :pin => "SECRET PIN")
34
+ blockio = BlockIo::Client.new(:api_key => "API KEY", :pin => "SECRET PIN")
36
35
 
37
- If you do not have your PIN, or just wish to use your private key backup directly, do this:
36
+ If you do not have your PIN, or just wish to use your private key backup(s) directly, do this instead:
38
37
 
39
- blockio = BlockIo::Client.new(:api_key => "API KEY", :keys => [BlockIo::Key.from_wif("PRIVATE KEY BACKUP")])
38
+ blockio = BlockIo::Client.new(:api_key => "API KEY")
39
+ blockio.get_balance
40
+ blockio.prepare_transaction(..., :keys => [BlockIo::Key.from_wif("PRIVATE_KEY_BACKUP_IN_WIF").private_key_hex])
40
41
 
41
42
  And you're good to go:
42
43
 
data/block_io.gemspec CHANGED
@@ -15,15 +15,15 @@ Gem::Specification.new do |spec|
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.required_ruby_version = '>= 2.3.0'
18
+ spec.required_ruby_version = '>= 2.4.0'
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", ">= 1.16", "< 3.0"
22
- spec.add_development_dependency "rake", "~> 12.3", ">= 12.3.3"
22
+ spec.add_development_dependency "rake", "~> 13.0", ">= 13.0"
23
23
  spec.add_development_dependency "rspec", "~> 3.6", ">= 3.6"
24
- spec.add_development_dependency "webmock", "~> 3.8", "< 4.0"
25
- spec.add_runtime_dependency "ecdsa", "~> 1.2.0", ">= 1.2.0"
24
+ spec.add_development_dependency "webmock", "~> 3.12", "< 4.0"
25
+ spec.add_runtime_dependency "bitcoinrb", "~> 0.7.0", "= 0.7.0"
26
26
  spec.add_runtime_dependency "http", "~> 4.4.1", ">= 4.4.1"
27
- spec.add_runtime_dependency "oj", "~> 3.10.6", ">= 3.10"
28
- spec.add_runtime_dependency "connection_pool", "~> 2.2.3", ">= 2.2"
27
+ spec.add_runtime_dependency "oj", "~> 3.11", ">= 3.11"
28
+ spec.add_runtime_dependency "connection_pool", "~> 2.2.5", ">= 2.2"
29
29
  end
data/examples/basic.rb CHANGED
@@ -3,18 +3,42 @@
3
3
  # basic example: $ API_KEY=TESTNET_API_KEY PIN=YOUR_SECRET_PIN ruby basic.rb
4
4
  # bundler example: $ API_KEY=TESTNET_API_KEY PIN=YOUR_SECRET_PIN bundle exec ruby basic.rb
5
5
  #
6
- # adjust amount below if not using the Dogecoin Testnet
6
+ # adjust amount below if not using the Litecoin Testnet
7
7
 
8
8
  require 'block_io'
9
9
 
10
- blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'], :pin => ENV['PIN'], :version => 2)
10
+ blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'], :pin => ENV['PIN'])
11
11
  puts blockio.get_balance
12
12
  puts blockio.network
13
13
 
14
14
  # create the address if it doesn't exist
15
- puts blockio.get_new_address(:label => 'testDest')
16
-
17
- puts blockio.withdraw_from_labels(:from_labels => 'default', :to_label => 'testDest', :amount => '2.5')
15
+ begin
16
+ puts blockio.get_new_address(:label => 'testDest')
17
+ rescue BlockIo::APIException => e
18
+ puts e.to_s
19
+ end
20
+ puts " -- "
21
+
22
+ # retrieve unspent outputs and other relevant data to create and sign the transaction
23
+ # you will inspect the prepared transaction for things like network fees being paid, block.io fees being paid, validating what destination addresses receive how much, etc.
24
+ prepared_transaction = blockio.prepare_transaction(:to_label => 'testDest', :amount => '0.012345')
25
+ puts JSON.pretty_generate(prepared_transaction)
26
+ puts " -- "
27
+
28
+ # a summary of what's in the prepared transaction
29
+ # for in-depth review of the transaction, look at the prepared_transaction object yourself
30
+ puts JSON.pretty_generate(blockio.summarize_prepared_transaction(prepared_transaction))
31
+ puts " -- "
32
+
33
+ # once satisfied with the prepared transaction, create and sign it
34
+ # inspect this again if you wish, it will contain the transaction payload in hexadecimal form you want Block.io to sign and broadcast
35
+ transaction_data = blockio.create_and_sign_transaction(prepared_transaction)
36
+ puts JSON.pretty_generate(transaction_data)
37
+ puts " -- "
38
+
39
+ # ask Block.io to sign and broadcast the transaction
40
+ puts JSON.pretty_generate(blockio.submit_transaction(:transaction_data => transaction_data))
41
+ puts " -- "
18
42
 
19
43
  puts blockio.get_address_balance(:labels => 'default,testDest')
20
44
 
data/examples/dtrust.rb CHANGED
@@ -1,25 +1,31 @@
1
- # creates a new destination address, withdraws from the default label to it, gets sent transactions, and the current price
1
+ # creates a new dTrust destination address, sends coins to it, withdraws coins from it, gets sent transactions, and the current price
2
2
 
3
3
  require 'block_io'
4
4
  require 'json'
5
5
 
6
6
  # please use the Litecoin Testnet API key here
7
7
  puts "*** Initialize BlockIo library: "
8
- blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'], :pin => ENV['PIN'], :version => 2)
8
+ blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'], :pin => ENV['PIN'])
9
+
9
10
  puts blockio.get_dtrust_balance
10
11
  puts blockio.network
11
12
 
12
13
  raise "Please use the LTCTEST network API Key here or modify this script for another network." unless blockio.network == "LTCTEST"
13
14
 
14
15
  # create 4 keys
15
- # you will generate your own private keys, for instance: key = BlockIo::Key.new. Just note down key.public_key and key.private_key somewhere safe before you use your keys to generate dTrust addresses.
16
- # if you already have hex private keys, load the keys with BlockIo::Key.new(private_key_hex). Ensure the key's .public_key matches what you expect.
16
+ # you will generate your own private keys, for instance: key = BlockIo::Key.generate.
17
+ # you will need to record key.to_wif (private key in Wallet Import Format (WIF)) somewhere safe before you use your keys to generate dTrust addresses.
18
+ # if you already have hex private keys, load the keys (see below). Ensure the key's public_key (key.public_key_hex) matches what you expect.
17
19
  # WARNING: The keys below are just for demonstration, DO NOT use them on mainnets, DO NOT use insecurely generated keys
20
+ # WARNING: You must ALWAYS use compressed public keys. Use of uncompressed public keys can lead to lost coins when using SegWit addresses.
21
+
22
+ # these keys will use the appropriate coin's parameters. The library will know what network you're interacting with once you make a successful API call first, like blockio.get_dtrust_balance above
23
+
18
24
  keys = [
19
- BlockIo::Key.new("b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2"), # alpha1alpha2alpha3alpha4
20
- BlockIo::Key.new("1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f"), # alpha2alpha3alpha4alpha1
21
- BlockIo::Key.new("2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61"), # alpha3alpha4alpha1alpha2
22
- BlockIo::Key.new("6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65") # alpha4alpha1alpha2alpha3
25
+ "b515fd806a662e061b488e78e5d0c2ff46df80083a79818e166300666385c0a2", # alpha1alpha2alpha3alpha4
26
+ "1584b821c62ecdc554e185222591720d6fe651ed1b820d83f92cdc45c5e21f", # alpha2alpha3alpha4alpha1
27
+ "2f9090b8aa4ddb32c3b0b8371db1b50e19084c720c30db1d6bb9fcd3a0f78e61", # alpha3alpha4alpha1alpha2
28
+ "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65" # alpha4alpha1alpha2alpha3
23
29
  ].freeze
24
30
 
25
31
  dtrust_address = nil
@@ -29,15 +35,13 @@ begin
29
35
  # let's create a new address with all 4 keys as signers, but only 3 signers required (i.e., 4 of 5 multisig, with 1 signature being Block.io)
30
36
  # you will need all 4 of your keys to use your address without interacting with Block.io
31
37
 
32
- signers = keys.map{|k| k.public_key}.join(',')
38
+ signers = keys.map{|x| BlockIo::Key.from_private_key_hex(x)}.map(&:public_key_hex).join(',')
33
39
 
34
40
  response = blockio.get_new_dtrust_address(:label => dtrust_address_label, :public_keys => signers, :required_signatures => 3, :address_type => "witness_v0")
35
41
 
36
42
  dtrust_address = response['data']['address']
37
43
 
38
- raise response["data"]["error_message"] unless response["status"].eql?("success")
39
-
40
- rescue Exception => e
44
+ rescue BlockIo::APIException => e
41
45
  # if this failed, we probably created the same label before. let's fetch the address then.
42
46
  puts e.to_s
43
47
 
@@ -49,7 +53,21 @@ end
49
53
  puts "*** Our dTrust Address: #{dtrust_address}"
50
54
 
51
55
  # let's deposit some coins into this new address
52
- response = blockio.withdraw_from_labels(:from_labels => 'default', :to_address => dtrust_address, :amount => '0.001')
56
+
57
+ # blockio.prepare_transaction gets the appropriate data you need to create and sign your transaction. You will need to inspect it to ensure things are as expected yourself.
58
+ prepared_transaction = blockio.prepare_transaction(:to_address => dtrust_address, :amount => '0.001')
59
+
60
+ puts JSON.pretty_generate(prepared_transaction)
61
+ puts " -- "
62
+
63
+ puts JSON.pretty_generate(blockio.summarize_prepared_transaction(prepared_transaction))
64
+ puts " -- "
65
+
66
+ # blockio.create_and_sign_transaction creates the transaction client-side, and appends your signatures (if any)
67
+ transaction_data = blockio.create_and_sign_transaction(prepared_transaction)
68
+
69
+ # blockio.submit_transaction sends the signatures and transaction payload to Block.io so Block.io can add its signatures and broadcast the transaction to the network
70
+ response = blockio.submit_transaction(:transaction_data => transaction_data)
53
71
 
54
72
  puts "*** Withdrawal response:"
55
73
  puts JSON.pretty_generate(response)
@@ -64,25 +82,26 @@ normal_address = blockio.get_address_by_label(:label => 'default')['data']['addr
64
82
 
65
83
  puts "*** Withdrawing from dtrust_address_label to the 'default' label in normal multisig"
66
84
 
67
- response = blockio.withdraw_from_dtrust_address(:from_labels => dtrust_address_label, :to_addresses => normal_address, :amounts => '0.0009')
68
-
69
- puts JSON.pretty_generate(response)
85
+ # note use of prepare_dtrust_transaction instead of prepare_transaction, since this is a dTrust transaction
86
+ # we're not doing any inspection here, but you will in your own code
87
+ prepared_transaction = blockio.prepare_dtrust_transaction(:from_labels => dtrust_address_label, :to_addresses => normal_address, :amounts => '0.0009')
70
88
 
71
- # let's sign for the public keys specified
72
- signatures_added = BlockIo::Helper.signData(response["data"]["inputs"], keys)
89
+ # create the transaction and sign it with the keys we've provided
90
+ # we're signing the transaction partially by supplying only 3 of our 4 keys
91
+ transaction_data = blockio.create_and_sign_transaction(prepared_transaction, keys.first(3))
73
92
 
74
- puts "*** Signatures added? #{signatures_added}"
93
+ puts "*** Submitting transaction data:"
94
+ puts JSON.pretty_generate(transaction_data)
75
95
 
76
- puts "*** Our (signed) request:"
77
- puts JSON.pretty_generate(response['data'])
96
+ # if successful, you will get a transaction ID when you submit_transaction.
97
+ response = blockio.submit_transaction(:transaction_data => transaction_data)
78
98
 
79
99
  # let's final the withdrawal
80
- puts "*** Finalize withdrawal: "
81
- puts JSON.pretty_generate(blockio.sign_and_finalize_withdrawal({:signature_data => response["data"]}))
100
+ puts "*** Submit the transaction: "
101
+ puts JSON.pretty_generate(response)
82
102
 
83
103
  # get the sent transactions for this dTrust address
84
104
 
85
105
  puts "*** Get transactions sent by our dtrust_address_label address: "
86
-
87
106
  puts JSON.pretty_generate(blockio.get_dtrust_transactions(:type => 'sent', :labels => dtrust_address_label))
88
107
 
data/examples/sweeper.rb CHANGED
@@ -4,26 +4,31 @@
4
4
  #
5
5
  # Contact support@block.io if you have any issues
6
6
 
7
- require "block_io"
7
+ require 'block_io'
8
8
 
9
- blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'], :version => 2)
10
- puts blockio.get_balance
11
- puts blockio.network
9
+ blockio = BlockIo::Client.new(:api_key => ENV['API_KEY'])
12
10
 
13
11
  to_address = ENV['TO_ADDRESS'] # sweep coins into this address
12
+ private_key = ENV['PRIVATE_KEY'] # private key for the address from which you wish to sweep coins (WIF)
14
13
 
15
- from_address = ENV['FROM_ADDRESS'] # sweep coins from this address
16
- private_key = ENV['PRIVATE_KEY'] # private key for from_address
14
+ # prepare the sweep transaction
15
+ # you will inspect this data to ensure things are in order (the network fees you pay, the amount being swept, etc.)
16
+ # the private key is used to determine the public key by prepare_sweep_transaction client-side
17
+ # the private key never travels to Block.io
18
+ prepared_transaction = blockio.prepare_sweep_transaction(:to_address => to_address, :private_key => private_key)
19
+ puts JSON.pretty_generate(prepared_transaction)
20
+ puts " -- "
17
21
 
18
- begin
19
- response = blockio.sweep_from_address(:to_address => to_address, :private_key => private_key, :from_address => from_address)
22
+ # create and sign the transaction
23
+ # the signature is from the key you provided to prepare_sweep_transaction above
24
+ transaction_data = blockio.create_and_sign_transaction(prepared_transaction)
25
+ puts JSON.pretty_generate(transaction_data)
26
+ puts " -- "
20
27
 
21
- raise response["data"]["error_message"] unless response["status"].eql?("success")
28
+ # submit the final transaction to Block.io for broadcast to the network, or
29
+ # submit the transaction payload youself elsewhere (like using sendrawtransaction RPC calls with bitcoind, dogecoind, litecoind, etc.)
30
+ response = blockio.submit_transaction(:transaction_data => transaction_data)
31
+ puts JSON.pretty_generate(response)
32
+ puts " -- "
22
33
 
23
- puts "Sweep Complete: #{response['data']['amount_sent']} #{response['data']['network']} swept from #{from_address} to #{to_address}."
24
- puts "Transaction ID: #{response['data']['txid']}"
25
- puts "Network Fee Incurred: #{response['data']['network_fee']} #{response['data']['network']}"
26
-
27
- rescue Exception => e
28
- puts "Sweep failed: #{e}"
29
- end
34
+ puts "Transaction ID: #{response['data']['txid']}"
data/lib/block_io.rb CHANGED
@@ -1,15 +1,16 @@
1
1
  require "http"
2
2
  require "oj"
3
- require "ecdsa"
3
+ require "bitcoin"
4
4
  require "openssl"
5
5
  require "securerandom"
6
6
  require "connection_pool"
7
7
 
8
8
  require_relative "block_io/version"
9
- require_relative "block_io/constants"
10
9
  require_relative "block_io/helper"
11
10
  require_relative "block_io/key"
12
11
  require_relative "block_io/client"
12
+ require_relative "block_io/api_exception"
13
+ require_relative "block_io/extended_bitcoinrb"
13
14
 
14
15
  module BlockIo
15
16
 
@@ -0,0 +1,11 @@
1
+ module BlockIo
2
+ class APIException < Exception
3
+
4
+ attr_reader :raw_data
5
+
6
+ def set_raw_data(data)
7
+ @raw_data = data
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "BTC"
3
+ address_version: "00"
4
+ p2sh_version: "05"
5
+ bech32_hrp: 'bc'
6
+ privkey_version: "80"
7
+ extended_privkey_version: "0488ade4"
8
+ extended_pubkey_version: "0488b21e"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "BTCTEST"
3
+ address_version: "6f"
4
+ p2sh_version: "c4"
5
+ bech32_hrp: 'tb'
6
+ privkey_version: "ef"
7
+ extended_privkey_version: "04358394"
8
+ extended_pubkey_version: "043587cf"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "DOGE"
3
+ address_version: "1e"
4
+ p2sh_version: "16"
5
+ bech32_hrp: "doge"
6
+ privkey_version: "9e"
7
+ extended_privkey_version: "02fac398"
8
+ extended_pubkey_version: "02facafd"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "DOGETEST"
3
+ address_version: "71"
4
+ p2sh_version: "c4"
5
+ bech32_hrp: "tdge"
6
+ privkey_version: "f1"
7
+ extended_privkey_version: "0432a243"
8
+ extended_pubkey_version: "0432a9a8"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "LTC"
3
+ address_version: "30"
4
+ p2sh_version: "05"
5
+ bech32_hrp: 'ltc'
6
+ privkey_version: "b0"
7
+ extended_privkey_version: "019d9cfe"
8
+ extended_pubkey_version: "019da462"
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Bitcoin::ChainParams
2
+ network: "LTCTEST"
3
+ address_version: "6f"
4
+ p2sh_version: "3a"
5
+ bech32_hrp: 'tltc'
6
+ privkey_version: "ef"
7
+ extended_privkey_version: "0436f6e1"
8
+ extended_pubkey_version: "0436ef7d"
@@ -20,16 +20,8 @@ module BlockIo
20
20
  @version = args[:version] || 2
21
21
  @hostname = args[:hostname] || "block.io"
22
22
  @proxy = args[:proxy] || {}
23
- @keys = args[:keys] || []
24
- @use_low_r = args[:use_low_r]
25
- @raise_exception_on_error = args[:raise_exception_on_error] || false
23
+ @keys = {}
26
24
 
27
- raise Exception.new("Keys must be provided as an array.") unless @keys.is_a?(Array)
28
- raise Exception.new("Keys must be BlockIo::Key objects.") unless @keys.all?{|key| key.is_a?(BlockIo::Key)}
29
-
30
- # make a hash of the keys we've been given
31
- @keys = @keys.inject({}){|h,v| h[v.public_key] = v; h}
32
-
33
25
  raise Exception.new("Must specify hostname, port, username, password if using a proxy.") if @proxy.keys.size > 0 and [:hostname, :port, :username, :password].any?{|x| !@proxy.key?(x)}
34
26
 
35
27
  @conn = ConnectionPool.new(:size => args[:pool_size] || 5) { http = HTTP.headers(:accept => "application/json", :user_agent => "gem:block_io:#{VERSION}");
@@ -50,109 +42,175 @@ module BlockIo
50
42
  raise Exception.new("Parameter keys must be symbols. For instance: :label => 'default' instead of 'label' => 'default'") unless args[0].nil? or args[0].keys.all?{|x| x.is_a?(Symbol)}
51
43
  raise Exception.new("Cannot pass PINs to any calls. PINs can only be set when initiating this library.") if !args[0].nil? and args[0].key?(:pin)
52
44
  raise Exception.new("Do not specify API Keys here. Initiate a new BlockIo object instead if you need to use another API Key.") if !args[0].nil? and args[0].key?(:api_key)
53
-
54
- if BlockIo::WITHDRAW_METHODS.key?(method_name) then
55
- # it's a withdrawal call
56
- withdraw(args[0], method_name)
57
- elsif BlockIo::SWEEP_METHODS.key?(method_name) then
45
+
46
+ if method_name.eql?("prepare_sweep_transaction") then
47
+ # we need to ensure @network is set before we allow this
48
+ # we need to send only the public key, not the given private key
58
49
  # we're sweeping from an address
59
- sweep(args[0], method_name)
60
- elsif BlockIo::FINALIZE_SIGNATURE_METHODS.key?(method_name) then
61
- # we're finalize the transaction signatures
62
- finalize_signature(args[0], method_name)
50
+ internal_prepare_sweep_transaction(args[0], method_name)
63
51
  else
64
52
  api_call({:method_name => method_name, :params => args[0] || {}})
65
53
  end
66
54
 
67
55
  end
68
56
 
69
- private
70
-
71
- def withdraw(args = {}, method_name = "withdraw")
57
+ def summarize_prepared_transaction(data)
58
+ # takes the response from prepare_transaction/prepare_dtrust_transaction/prepare_sweep_transaction
59
+ # returns the network fee being paid, the blockio fee being paid, amounts being sent
72
60
 
73
- response = api_call({:method_name => method_name, :params => args})
61
+ input_sum = data['data']['inputs'].map{|input| BigDecimal(input['input_value'])}.inject(:+)
74
62
 
75
- if response["data"].key?("reference_id") then
76
- # Block.io's asking us to provide client-side signatures
63
+ output_values = [BigDecimal(0)]
64
+ blockio_fees = [BigDecimal(0)]
65
+ change_amounts = [BigDecimal(0)]
77
66
 
78
- encrypted_passphrase = response["data"]["encrypted_passphrase"]
79
-
80
- if !encrypted_passphrase.nil? and !@keys.key?(encrypted_passphrase["signer_public_key"]) then
81
- # encrypted passphrase was provided, and we do not have the signer's key, so let's extract it first
67
+ data['data']['outputs'].each do |output|
68
+ if output['output_category'] == 'blockio-fee' then
69
+ blockio_fees << BigDecimal(output['output_value'])
70
+ elsif output['output_category'] == 'change' then
71
+ change_amounts << BigDecimal(output['output_value'])
72
+ else
73
+ # user-specified
74
+ output_values << BigDecimal(output['output_value'])
75
+ end
76
+ end
77
+
78
+ output_sum = output_values.inject(:+)
79
+ blockio_fee = blockio_fees.inject(:+)
80
+ change_amount = change_amounts.inject(:+)
81
+
82
+ network_fee = input_sum - output_sum - blockio_fee - change_amount
83
+
84
+ {
85
+ 'network' => data['data']['network'],
86
+ 'network_fee' => '%0.8f' % network_fee,
87
+ "blockio_fee" => '%0.8f' % blockio_fee,
88
+ "total_amount_to_send" => '%0.8f' % output_sum
89
+ }
90
+
91
+ end
92
+
93
+ def create_and_sign_transaction(data, keys = [])
94
+ # takes data from prepare_transaction, prepare_dtrust_transaction, prepare_sweep_transaction
95
+ # creates the transaction given the inputs and outputs from data
96
+ # signs the transaction using keys (if not provided, decrypts the key using the PIN)
97
+
98
+ set_network(data['data']['network']) if data['data'].key?('network')
82
99
 
83
- raise Exception.new("PIN not set and no keys provided. Cannot execute withdrawal requests.") unless @encryption_key or @keys.size > 0
100
+ raise "Data must be contain one or more inputs" unless data['data']['inputs'].size > 0
101
+ raise "Data must contain one or more outputs" unless data['data']['outputs'].size > 0
102
+ raise "Data must contain information about addresses" unless data['data']['input_address_data'].size > 0 # TODO make stricter
84
103
 
85
- key = Helper.extractKey(encrypted_passphrase["passphrase"], @encryption_key, @use_low_r)
86
- raise Exception.new("Public key mismatch for requested signer and ourselves. Invalid Secret PIN detected.") unless key.public_key.eql?(encrypted_passphrase["signer_public_key"])
104
+ private_keys = keys.map{|x| Key.from_private_key_hex(x)}
87
105
 
88
- # store this key for later use
89
- @keys[key.public_key] = key
106
+ # TODO debug all of this
107
+
108
+ inputs = data['data']['inputs']
109
+ outputs = data['data']['outputs']
90
110
 
91
- end
111
+ tx = Bitcoin::Tx.new
92
112
 
93
- if @keys.size > 0 then
94
- # if we have at least one key available, try to send signatures back
95
- # if a dtrust withdrawal is used without any keys stored in the BlockIo::Client object, the output of this call will be the previous response from Block.io
96
-
97
- # we just need reference_id and inputs
98
- response["data"] = {"reference_id" => response["data"]["reference_id"], "inputs" => response["data"]["inputs"]}
99
-
100
- # let's sign all the inputs we can
101
- signatures_added = (@keys.size == 0 ? false : Helper.signData(response["data"]["inputs"], @keys))
102
-
103
- # the response object is now signed, let's stringify it and finalize this withdrawal
104
- response = finalize_signature({:signature_data => response["data"]}, "sign_and_finalize_withdrawal") if signatures_added
105
-
106
- # if we provided all the required signatures, this transaction went through
107
- # otherwise Block.io responded with data asking for more signatures and recorded the signature we provided above
108
- # the latter will be the case for dTrust addresses
109
- end
110
-
113
+ # populate the inputs
114
+ inputs.each do |input|
115
+ tx.in << Bitcoin::TxIn.new(:out_point => Bitcoin::OutPoint.from_txid(input['previous_txid'], input['previous_output_index']))
111
116
  end
112
117
 
113
- response
118
+ # populate the outputs
119
+ outputs.each do |output|
120
+ tx.out << Bitcoin::TxOut.new(:value => (BigDecimal(output['output_value']) * BigDecimal(100000000)).to_i, :script_pubkey => Bitcoin::Script.parse_from_addr(output['receiving_address']))
121
+ end
114
122
 
115
- end
116
123
 
117
- def sweep(args = {}, method_name = "sweep_from_address")
118
- # sweep coins from a given address and key
124
+ # some protection against misbehaving machines and/or code
125
+ raise Exception.new("Expected unsigned transaction ID mismatch. Please report this error to support@block.io.") unless (data['data']['expected_unsigned_txid'].nil? or
126
+ data['data']['expected_unsigned_txid'] == tx.txid)
119
127
 
120
- raise Exception.new("No private_key provided.") unless args.key?(:private_key) and (args[:private_key] || "").size > 0
128
+ # extract key
129
+ encrypted_key = data['data']['user_key']
121
130
 
122
- key = Key.from_wif(args[:private_key], @use_low_r)
123
- sanitized_args = args.merge({:public_key => key.public_key})
124
- sanitized_args.delete(:private_key)
125
-
126
- response = api_call({:method_name => method_name, :params => sanitized_args})
127
-
128
- if response["data"].key?("reference_id") then
129
- # Block.io's asking us to provide client-side signatures
131
+ if !encrypted_key.nil? and !@keys.key?(encrypted_key['public_key']) then
132
+ # decrypt the key with PIN
130
133
 
131
- # we just need the reference_id and inputs
132
- response["data"] = {"reference_id" => response["data"]["reference_id"], "inputs" => response["data"]["inputs"]}
134
+ raise Exception.new("PIN not set and no keys provided. Cannot sign transaction.") unless @encryption_key or @keys.size > 0
133
135
 
134
- # let's sign all the inputs we can
135
- signatures_added = Helper.signData(response["data"]["inputs"], [key])
136
+ key = Helper.extractKey(encrypted_key['encrypted_passphrase'], @encryption_key)
137
+ raise Exception.new("Public key mismatch for requested signer and ourselves. Invalid Secret PIN detected.") unless key.public_key_hex.eql?(encrypted_key["public_key"])
136
138
 
137
- # the response object is now signed, let's stringify it and finalize this transaction
138
- response = finalize_signature({:signature_data => response["data"]}, "sign_and_finalize_sweep") if signatures_added
139
+ # store this key for later use
140
+ @keys[key.public_key_hex] = key
141
+
142
+ end
139
143
 
140
- # if we provided all the required signatures, this transaction went through
144
+ # store the provided keys, if any, for later use
145
+ private_keys.each{|key| @keys[key.public_key_hex] = key}
146
+
147
+ signatures = []
148
+
149
+ if @keys.size > 0 then
150
+ # try to sign whatever we can here and give the user the data back
151
+ # Block.io will check to see if all signatures are present, or return an error otherwise saying insufficient signatures provided
152
+
153
+ i = 0
154
+ while i < inputs.size do
155
+ input = inputs[i]
156
+
157
+ input_address_data = data['data']['input_address_data'].detect{|d| d['address'] == input['spending_address']}
158
+ sighash_for_input = Helper.getSigHashForInput(tx, i, input, input_address_data) # in bytes
159
+
160
+ input_address_data['public_keys'].each do |signer_public_key|
161
+ # sign what we can and append signatures to the signatures object
162
+
163
+ next unless @keys.key?(signer_public_key)
164
+
165
+ signature = @keys[signer_public_key].sign(sighash_for_input).unpack("H*")[0] # in hex
166
+ signatures << {"input_index" => i, "public_key" => signer_public_key, "signature" => signature}
167
+
168
+ end
169
+
170
+ i += 1 # go to next input
171
+ end
172
+
141
173
  end
142
174
 
143
- response
175
+ # if we have everything we need for this transaction, just finalize the transaction
176
+ if Helper.allSignaturesPresent?(tx, inputs, signatures, data['data']['input_address_data']) then
177
+ Helper.finalizeTransaction(tx, inputs, signatures, data['data']['input_address_data'])
178
+ signatures = [] # no signatures left to append
179
+ end
144
180
 
181
+ # reset keys
182
+ @keys = {}
183
+
184
+ # the response for submitting the transaction
185
+ {"tx_type" => data['data']['tx_type'], "tx_hex" => tx.to_hex, "signatures" => (signatures.size == 0 ? nil : signatures)}
186
+
145
187
  end
146
188
 
147
- def finalize_signature(args = {}, method_name = "sign_and_finalize_withdrawal")
189
+ private
190
+
191
+ def internal_prepare_sweep_transaction(args = {}, method_name = "prepare_sweep_transaction")
148
192
 
149
- raise Exception.new("Object must have reference_id and inputs keys.") unless args.key?(:signature_data) and args[:signature_data].key?("inputs") and args[:signature_data].key?("reference_id")
193
+ # set the network first if not already known
194
+ api_call({:method_name => "get_balance", :params => {}}) if @network.nil?
150
195
 
151
- signatures = {"reference_id" => args[:signature_data]["reference_id"], "inputs" => args[:signature_data]["inputs"]}
196
+ raise Exception.new("No private_key provided.") unless args.key?(:private_key) and (args[:private_key] || "").size > 0
152
197
 
153
- response = api_call({:method_name => method_name, :params => {:signature_data => Oj.dump(signatures)}})
198
+ # ensure the private key never goes to Block.io
199
+ key = Key.from_wif(args[:private_key])
200
+ sanitized_args = args.merge({:public_key => key.public_key_hex})
201
+ sanitized_args.delete(:private_key)
202
+
203
+ @keys[key.public_key_hex] = key # store this in our set of keys for later use
204
+
205
+ api_call({:method_name => method_name, :params => sanitized_args})
154
206
 
155
207
  end
208
+
209
+ def set_network(network)
210
+ # load the chain_params for this network
211
+ @network ||= network
212
+ Bitcoin.chain_params = @network unless @network.to_s.size == 0
213
+ end
156
214
 
157
215
  def api_call(args)
158
216
 
@@ -166,9 +224,15 @@ module BlockIo
166
224
  body = {"status" => "fail", "data" => {"error_message" => "Unknown error occurred. Please report this to support@block.io. Status #{response.code}."}}
167
225
  end
168
226
 
169
- raise Exception.new("#{body["data"]["error_message"]}") if !body["status"].eql?("success") and @raise_exception_on_error
227
+ if !body["status"].eql?("success") then
228
+ # raise an exception on error for easy handling
229
+ # user can extract raw response using e.raw_data
230
+ e = APIException.new("#{body["data"]["error_message"]}")
231
+ e.set_raw_data(body)
232
+ raise e
233
+ end
170
234
 
171
- @network ||= body["data"]["network"] if body["data"].key?("network")
235
+ set_network(body['data']['network']) if body['data'].key?('network')
172
236
 
173
237
  body
174
238