bitcoin 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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