bitcoin 0.1.0 → 0.1.2

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.
Files changed (64) hide show
  1. data/.rvmrc +1 -1
  2. data/Gemfile.lock +56 -4
  3. data/Guardfile +38 -0
  4. data/README.markdown +21 -2
  5. data/bin/rbcoin +8 -1
  6. data/bitcoin.gemspec +17 -5
  7. data/config/cucumber.yml +3 -3
  8. data/config/darcs.boring +121 -0
  9. data/doc/DEFINITION_OF_DONE.markdown +12 -0
  10. data/doc/HISTORY.markdown +19 -0
  11. data/{LICENCE.markdown → doc/LICENCE.markdown} +1 -1
  12. data/doc/TODO.markdown +31 -0
  13. data/doc/UBIQUITOUS_LANGUAGE.markdown +15 -0
  14. data/features/descriptions/command_help.feature +31 -0
  15. data/features/descriptions/satoshi_wallet/add_address.feature +49 -0
  16. data/features/descriptions/satoshi_wallet/show_addresses.feature +18 -0
  17. data/features/descriptions/satoshi_wallet/show_version.feature +17 -0
  18. data/features/descriptions/satoshi_wallet/subcommand_help.feature +20 -0
  19. data/features/fixtures/ABOUT_FIXTURES.markdown +6 -0
  20. data/features/fixtures/addressbook_wallet.dat +0 -0
  21. data/features/fixtures/new_wallet.dat +0 -0
  22. data/features/step_definitions/command_steps.rb +3 -0
  23. data/features/step_definitions/wallet_steps.rb +11 -0
  24. data/features/support/env.rb +8 -1
  25. data/lib/bitcoin/cli.rb +35 -0
  26. data/lib/bitcoin/commands.rb +3 -0
  27. data/lib/bitcoin/commands/help_command.rb +32 -0
  28. data/lib/bitcoin/commands/satoshi_wallet.rb +11 -0
  29. data/lib/bitcoin/commands/satoshi_wallet/add_address_command.rb +61 -0
  30. data/lib/bitcoin/commands/satoshi_wallet/show_addresses_command.rb +16 -0
  31. data/lib/bitcoin/commands/satoshi_wallet/show_version_command.rb +15 -0
  32. data/lib/bitcoin/commands/satoshi_wallet_command.rb +37 -0
  33. data/lib/bitcoin/commands/satoshi_wallet_command_environment.rb +28 -0
  34. data/lib/bitcoin/console/capturing_stream_bundle.rb +42 -0
  35. data/lib/bitcoin/console/stream_bundle.rb +21 -0
  36. data/lib/bitcoin/data_access/satoshi/bdb_satoshi_wallet_repository.rb +155 -0
  37. data/lib/bitcoin/data_access/satoshi/satoshi_version.rb +58 -0
  38. data/lib/bitcoin/data_access/satoshi/satoshi_wallet.rb +39 -0
  39. data/lib/bitcoin/domain/address_book.rb +19 -0
  40. data/lib/bitcoin/domain/bitcoin_address.rb +33 -0
  41. data/lib/bitcoin/filesystem/empty_temp_dir.rb +74 -0
  42. data/lib/bitcoin/rspec/argument_matchers.rb +1 -0
  43. data/lib/bitcoin/rspec/argument_matchers/block_evaluating_to_matcher.rb +23 -0
  44. data/lib/bitcoin/rspec/directory_helpers.rb +22 -0
  45. data/lib/bitcoin/version.rb +1 -1
  46. data/spec/bitcoin/cli_spec.rb +128 -0
  47. data/spec/bitcoin/commands/help_command_spec.rb +53 -0
  48. data/spec/bitcoin/commands/satoshi_wallet/add_address_command_spec.rb +149 -0
  49. data/spec/bitcoin/commands/satoshi_wallet/show_addresses_command_spec.rb +26 -0
  50. data/spec/bitcoin/commands/satoshi_wallet/show_version_command_spec.rb +26 -0
  51. data/spec/bitcoin/commands/satoshi_wallet_command_environment_spec.rb +76 -0
  52. data/spec/bitcoin/commands/satoshi_wallet_command_spec.rb +73 -0
  53. data/spec/bitcoin/console/_contracts/stream_bundle_contract.rb +29 -0
  54. data/spec/bitcoin/console/capturing_stream_bundle_spec.rb +74 -0
  55. data/spec/bitcoin/console/stream_bundle_spec.rb +13 -0
  56. data/spec/bitcoin/data_access/satoshi/bdb_satoshi_wallet_repository_spec.rb +78 -0
  57. data/spec/bitcoin/data_access/satoshi/satoshi_version_spec.rb +112 -0
  58. data/spec/bitcoin/data_access/satoshi/satoshi_wallet_spec.rb +102 -0
  59. data/spec/bitcoin/domain/address_book_spec.rb +63 -0
  60. data/spec/bitcoin/domain/bitcoin_address_spec.rb +52 -0
  61. data/spec/bitcoin/filesystem/empty_temp_dir_spec.rb +170 -0
  62. data/spec/bitcoin/rspec/argument_matchers/block_evaluating_to_matcher_spec.rb +36 -0
  63. data/spec/spec_helper.rb +29 -1
  64. metadata +221 -18
@@ -0,0 +1,49 @@
1
+ Feature: satoshi-wallet add-address
2
+ In order to reduce the risk of using RbCoin
3
+ As a Bitcoin owner
4
+ I want to be able to add new Addresses into a Satoshi Wallet
5
+
6
+ Scenario: Add new address
7
+ Given a new Satoshi Wallet "new_wallet.dat"
8
+ When I successfully run `rbcoin satoshi-wallet add-address new_wallet.dat 14Z1mazY4HfysZyMaKudFr63EwHqQT2njz "Bitcoin Monitor"`
9
+ Then the output should contain "Address added successfully"
10
+ When I successfully run `rbcoin satoshi-wallet show-addresses new_wallet.dat`
11
+ Then the output should contain:
12
+ """
13
+ # All addresses in new_wallet.dat
14
+ 14Z1mazY4HfysZyMaKudFr63EwHqQT2njz Bitcoin Monitor
15
+ """
16
+
17
+ Scenario: Invalid address
18
+ Given a new Satoshi Wallet "new_wallet.dat"
19
+ When I run `rbcoin satoshi-wallet add-address new_wallet.dat 1thisisaninvalidaddress "Bitcoin Monitor"`
20
+ Then the command should fail
21
+ And the stderr should contain:
22
+ """
23
+ The address "1thisisaninvalidaddress" is not a valid Bitcoin address
24
+ The address was not added to the wallet
25
+ """
26
+
27
+ Scenario: Missing arguments
28
+ Given a new Satoshi Wallet "new_wallet.dat"
29
+ When I run `rbcoin satoshi-wallet add-address new_wallet.dat 14Z1mazY4HfysZyMaKudFr63EwHqQT2njz`
30
+ Then the command should fail
31
+ And the stderr should contain:
32
+ """
33
+ You must provide both a Bitcoin address and a name to add to a wallet
34
+ No changes were made to the wallet
35
+ """
36
+
37
+ Scenario: Too many arguments
38
+ Given a new Satoshi Wallet "new_wallet.dat"
39
+ When I run `rbcoin satoshi-wallet add-address new_wallet.dat 14Z1mazY4HfysZyMaKudFr63EwHqQT2njz My name without quotes`
40
+ Then the command should fail
41
+ And the stderr should contain:
42
+ """
43
+ Received too many arguments
44
+
45
+ To make sure the name is formatted correctly we ask you to use use quotes
46
+ around the name, e.g.: "My name without quotes"
47
+
48
+ No changes were made to the wallet
49
+ """
@@ -0,0 +1,18 @@
1
+ Feature: satoshi-wallet show-addresses
2
+ In order to enable easily transitioning from the Satoshi Client
3
+ As a Bitcoin owner
4
+ I want to inspect the contents of my Satoshi Wallet
5
+
6
+ Scenario: Viewing all addresses
7
+ # addressbook_wallet.dat is a wallet with some known addresses but no transactions
8
+ Given a new Satoshi Wallet "addressbook_wallet.dat"
9
+ When I successfully run `rbcoin satoshi-wallet show-addresses addressbook_wallet.dat`
10
+ # I don't know what drives the address book order yet - I think it's the address itself
11
+ Then the output should contain:
12
+ """
13
+ # All addresses in addressbook_wallet.dat
14
+ 13f2t2WKnCZh1rZbDhdUKB6fY1rZNWZVzd Receiving address 2
15
+ 14Z1mazY4HfysZyMaKudFr63EwHqQT2njz Bitcoin Monitor
16
+ 16kzGRdP8LToECecna3EynghiApUM9duLw Receiving address 1
17
+ 1Nqr3MqVyUp6k3o3QPePAdn4Yg4tzgB9kw Bitcoincharts
18
+ """
@@ -0,0 +1,17 @@
1
+ Feature: satoshi-wallet show-version
2
+ In order to enable easily transitioning from the Satoshi Client
3
+ As a Bitcoin owner
4
+ I want to inspect the contents of my Satoshi Wallet
5
+
6
+ Scenario: Valid wallet file
7
+ # Which just so happens to be a 0.3.24 file...
8
+ Given a new Satoshi Wallet "new_wallet.dat"
9
+ When I successfully run `rbcoin satoshi-wallet show-version new_wallet.dat`
10
+ Then the output should contain "0.3.24"
11
+
12
+ Scenario: Missing wallet file
13
+ When I run `rbcoin satoshi-wallet show-version this_wallet_does_not_exist.dat`
14
+ Then it should fail with:
15
+ """
16
+ Couldn't find wallet file: this_wallet_does_not_exist.dat
17
+ """
@@ -0,0 +1,20 @@
1
+ Feature: Subcommand help for `rbcoin satoshi-wallet`
2
+ In order to avoid being frustrated by being unable to interact with the application
3
+ As a user
4
+ I want to be told when I make a mistake
5
+
6
+ Scenario: `rbcoin satoshi-wallet unknown-subcommand`
7
+ When I run `rbcoin satoshi-wallet unknown-subcommand`
8
+ Then the command should fail
9
+ And the stderr should contain:
10
+ """
11
+ Unknown satoshi-wallet subcommand: "unknown-subcommand"
12
+ """
13
+
14
+ # Scenario: `rbcoin help satoshi-wallet`
15
+ # When I successfully run `rbcoin satoshi-wallet help`
16
+ # Then the output should contain "Usage: TODO"
17
+ #
18
+ # Scenario: `rbcoin satoshi-wallet --help`
19
+ # When I successfully run `rbcoin satoshi-wallet --help`
20
+ # Then the output should contain "Usage: TODO"
@@ -0,0 +1,6 @@
1
+ # About the fixtures
2
+
3
+ Fixtures are bad, mmmkay?
4
+
5
+ But until we have a bulletproof means of writing a full and correct Satoshi Wallet
6
+ file, it's safer to use fixtures than to rely on our unproven Berkeley DB code.
@@ -0,0 +1,3 @@
1
+ Then %r/the command should fail/ do
2
+ Then "the exit status should be 1"
3
+ end
@@ -0,0 +1,11 @@
1
+ module Fixtures
2
+ def fixture_file_data(filename)
3
+ File.read(File.join(PROJECT_ROOT, "features", "fixtures", filename))
4
+ end
5
+ end
6
+
7
+ World(Fixtures)
8
+
9
+ Given %r/^a new Satoshi Wallet "([^"]*)"$/ do |wallet_filename|
10
+ write_file(wallet_filename, fixture_file_data(wallet_filename))
11
+ end
@@ -1,2 +1,9 @@
1
1
  require 'bundler'
2
- Bundler.setup
2
+ Bundler.setup
3
+
4
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..", "..")).freeze
5
+
6
+ # So Aruba can find the binary
7
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
8
+
9
+ require 'aruba/cucumber'
@@ -0,0 +1,35 @@
1
+ require 'bitcoin/commands'
2
+
3
+ module Bitcoin
4
+ class CLI
5
+ class CommandError < RuntimeError; end
6
+
7
+ def initialize(stream_bundle)
8
+ @stream_bundle = stream_bundle
9
+ end
10
+
11
+ def run(args)
12
+ command_name, *remaining_args = *args
13
+ dispatch_command(command_name, remaining_args)
14
+ rescue CommandError => error
15
+ 1
16
+ else
17
+ 0
18
+ end
19
+
20
+ private
21
+
22
+ def dispatch_command(command_name, remaining_args)
23
+ case command_name
24
+ when nil, "help"
25
+ Commands::HelpCommand.new(@stream_bundle).show_help
26
+ when "satoshi-wallet"
27
+ command_environment = Commands::SatoshiWalletCommandEnvironment.new(@stream_bundle)
28
+ Commands::SatoshiWalletCommand.new(@stream_bundle, command_environment).run(remaining_args)
29
+ else
30
+ Commands::HelpCommand.new(@stream_bundle).show_unknown_command_help(command_name)
31
+ raise CommandError.new
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'commands/help_command'
2
+ require_relative 'commands/satoshi_wallet_command'
3
+ require_relative 'commands/satoshi_wallet_command_environment'
@@ -0,0 +1,32 @@
1
+ require 'lstrip-on-steroids'
2
+
3
+ module Bitcoin
4
+ module Commands
5
+ class HelpCommand
6
+ def initialize(stream_bundle)
7
+ @stream_bundle = stream_bundle
8
+ end
9
+
10
+ def show_help
11
+ @stream_bundle.puts_output(
12
+ -%{
13
+ Usage: rbcoin COMMAND ...
14
+
15
+ Commands:
16
+ help Display help about rbcoin and rbcoin commands
17
+ satoshi-wallet Manipulate the wallet.dat file of the original client
18
+
19
+ Use "rbcoin COMMAND --help" or "rbcoin help COMMAND" for help on a single command
20
+ Use "rbcoin --version" to see the rbcoin version number
21
+ Use "rbcoin --exact-version" to see the all version details (with dependencies)
22
+ }
23
+ )
24
+ end
25
+
26
+ def show_unknown_command_help(command_name)
27
+ @stream_bundle.puts_output(%'rbcoin failed: No such command "#{command_name}"')
28
+ @stream_bundle.puts_output("Try: `rbcoin help`")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module Bitcoin
2
+ module Commands
3
+ module SatoshiWallet
4
+
5
+ end
6
+ end
7
+ end
8
+
9
+ require_relative 'satoshi_wallet/add_address_command'
10
+ require_relative 'satoshi_wallet/show_addresses_command'
11
+ require_relative 'satoshi_wallet/show_version_command'
@@ -0,0 +1,61 @@
1
+ require 'bitcoin/cli' # For CommandError
2
+
3
+ require 'bitcoin/domain/bitcoin_address'
4
+
5
+ module Bitcoin
6
+ module Commands
7
+ module SatoshiWallet
8
+ class AddAddressCommand
9
+ def initialize(stream_bundle)
10
+ @stream_bundle = stream_bundle
11
+ end
12
+
13
+ def run(wallet, options)
14
+ assert_valid_args(options[:args])
15
+
16
+ address, name = *options[:args]
17
+
18
+ assert_valid_name(name)
19
+
20
+ wallet.add_address(Domain::BitcoinAddress.new(address), name: name)
21
+ @stream_bundle.puts_output("Address added successfully")
22
+ rescue Domain::BitcoinAddress::InvalidBitcoinAddressError => error
23
+ @stream_bundle.puts_error(error.message)
24
+ @stream_bundle.puts_error("The address was not added to the wallet")
25
+ raise CLI::CommandError.new
26
+ end
27
+
28
+ private
29
+
30
+ def assert_valid_args(args)
31
+ if args.length < 2
32
+ @stream_bundle.puts_error("You must provide both a Bitcoin address and a name to add to a wallet")
33
+ @stream_bundle.puts_error("No changes were made to the wallet")
34
+ raise CLI::CommandError.new
35
+ elsif args.length > 2
36
+ @stream_bundle.puts_error(
37
+ -%{
38
+ Received too many arguments
39
+
40
+ To make sure the name is formatted correctly we ask you to use use quotes
41
+ around the name, e.g.: "#{args.drop(1).join(" ")}"
42
+
43
+ No changes were made to the wallet
44
+ }
45
+ )
46
+ raise CLI::CommandError.new
47
+ end
48
+
49
+ def assert_valid_name(name)
50
+ if name.bytesize > 252
51
+ @stream_bundle.puts_error(%'The address name "#{name}" is too long\n')
52
+ @stream_bundle.puts_error("Currently we do not allow address names over 252 bytes long\n")
53
+ @stream_bundle.puts_error("The address was not added to the wallet\n")
54
+ raise CLI::CommandError.new
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ module Bitcoin
2
+ module Commands
3
+ module SatoshiWallet
4
+ class ShowAddressesCommand
5
+ def initialize(stream_bundle)
6
+ @stream_bundle = stream_bundle
7
+ end
8
+
9
+ def run(wallet, options)
10
+ @stream_bundle.puts_output("# All addresses in #{options[:wallet_filename]}")
11
+ @stream_bundle.puts_output(wallet.addresses)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module Bitcoin
2
+ module Commands
3
+ module SatoshiWallet
4
+ class ShowVersionCommand
5
+ def initialize(stream_bundle)
6
+ @stream_bundle = stream_bundle
7
+ end
8
+
9
+ def run(wallet, options = { })
10
+ @stream_bundle.puts_output(wallet.version)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ require 'tmpdir'
2
+
3
+ require 'bitcoin/cli' # circular dependency on CLI for CommandError
4
+ require 'bitcoin/commands/satoshi_wallet'
5
+ require 'bitcoin/data_access/satoshi/satoshi_wallet'
6
+ require 'bitcoin/domain/bitcoin_address'
7
+ require 'bitcoin/filesystem/empty_temp_dir'
8
+
9
+ module Bitcoin
10
+ module Commands
11
+ class SatoshiWalletCommand
12
+ TEMP_DB_PATH = "bitcoinrb_db_tmp"
13
+
14
+ def initialize(stream_bundle, command_environment)
15
+ @stream_bundle = stream_bundle
16
+ @command_environment = command_environment
17
+ end
18
+
19
+ def run(args)
20
+ command_name, *remaining_args = *args
21
+ command_class =
22
+ case command_name
23
+ when "add-address"
24
+ SatoshiWallet::AddAddressCommand
25
+ when "show-addresses"
26
+ SatoshiWallet::ShowAddressesCommand
27
+ when "show-version"
28
+ SatoshiWallet::ShowVersionCommand
29
+ else
30
+ @stream_bundle.puts_error(%'Unknown satoshi-wallet subcommand: "#{command_name}"')
31
+ raise CLI::CommandError.new
32
+ end
33
+ @command_environment.run_command(command_class.new(@stream_bundle), remaining_args)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ require 'bitcoin/filesystem/empty_temp_dir'
2
+
3
+ module Bitcoin
4
+ module Commands
5
+ class SatoshiWalletCommandEnvironment
6
+ def initialize(stream_bundle)
7
+ @stream_bundle = stream_bundle
8
+ end
9
+
10
+ def run_command(command, arguments)
11
+ wallet_filename, *remaining_args = *arguments
12
+
13
+ if !File.exist?(wallet_filename)
14
+ @stream_bundle.puts_error("Couldn't find wallet file: #{wallet_filename}")
15
+ raise CLI::CommandError.new
16
+ end
17
+
18
+ absolute_wallet_path = File.expand_path(wallet_filename)
19
+
20
+ FileSystem::EmptyTempDir.open(with_name: "bitcoinrb_db_tmp", parallel_to: absolute_wallet_path) do |temp_dir|
21
+ DataAccess::Satoshi::SatoshiWallet.open(wallet_filename: absolute_wallet_path, db_dirname: temp_dir.absolute_path) do |wallet|
22
+ command.run(wallet, wallet_filename: wallet_filename, args: remaining_args)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'stream_bundle'
2
+
3
+ module Bitcoin
4
+ module Console
5
+ # CapturingStreamBundles are StreamBundles that record the data written to them
6
+ # for easy inspection later. They are useful for testing purposes.
7
+ class CapturingStreamBundle
8
+ class << self
9
+ def test_bundle
10
+ new(StringIO.new, StringIO.new, StringIO.new)
11
+ end
12
+ end
13
+
14
+ def initialize(input, output, error)
15
+ @stream_bundle = StreamBundle.new(input, output, error)
16
+
17
+ @captured_output = StringIO.new
18
+ @captured_error = StringIO.new
19
+ end
20
+
21
+ def puts_output(stringlike)
22
+ @stream_bundle.puts_output(stringlike)
23
+ @captured_output.puts(stringlike)
24
+ end
25
+
26
+ def puts_error(stringlike)
27
+ @stream_bundle.puts_error(stringlike)
28
+ @captured_error.puts(stringlike)
29
+ end
30
+
31
+ def captured_output
32
+ @captured_output.rewind
33
+ @captured_output.read
34
+ end
35
+
36
+ def captured_error
37
+ @captured_error.rewind
38
+ @captured_error.read
39
+ end
40
+ end
41
+ end
42
+ end