Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 7537399a2e73cadcc09a7d89ff95231d89ca42d5 0 parents
@davout authored
Showing with 14,049 additions and 0 deletions.
  1. +9 −0 .gitignore
  2. +4 −0 Capfile
  3. +21 −0 Gemfile
  4. +115 −0 Gemfile.lock
  5. +256 −0 README
  6. +7 −0 Rakefile
  7. +2 −0  app/controllers/accounts_controller.rb
  8. +5 −0 app/controllers/addresses_controller.rb
  9. +5 −0 app/controllers/admin/servers_controller.rb
  10. +43 −0 app/controllers/application_controller.rb
  11. +23 −0 app/controllers/bitcoin_transfers_controller.rb
  12. +48 −0 app/controllers/charts_controller.rb
  13. +7 −0 app/controllers/informations_controller.rb
  14. +50 −0 app/controllers/liberty_reserve_transfers_controller.rb
  15. +24 −0 app/controllers/sessions_controller.rb
  16. +37 −0 app/controllers/trade_orders_controller.rb
  17. +11 −0 app/controllers/trades_controller.rb
  18. +5 −0 app/controllers/transfers_controller.rb
  19. +40 −0 app/controllers/users_controller.rb
  20. +2 −0  app/helpers/accounts_helper.rb
  21. +2 −0  app/helpers/addresses_helper.rb
  22. +2 −0  app/helpers/admin/server_helper.rb
  23. +31 −0 app/helpers/application_helper.rb
  24. +2 −0  app/helpers/bitcoin_transfers_helper.rb
  25. +5 −0 app/helpers/charts_helper.rb
  26. +2 −0  app/helpers/informations_helper.rb
  27. +2 −0  app/helpers/liberty_reserve_transfer_helper.rb
  28. +2 −0  app/helpers/sessions_helper.rb
  29. +2 −0  app/helpers/trade_orders_helper.rb
  30. +2 −0  app/helpers/trades_helper.rb
  31. +2 −0  app/helpers/transfers_helper.rb
  32. +2 −0  app/helpers/users_helper.rb
  33. +12 −0 app/mailers/user_mailer.rb
  34. +88 −0 app/models/bitcoin_transfer.rb
  35. +58 −0 app/models/liberty_reserve_transfer.rb
  36. +76 −0 app/models/trade.rb
  37. +164 −0 app/models/trade_order.rb
  38. +80 −0 app/models/transfer.rb
  39. +115 −0 app/models/user.rb
  40. +86 −0 app/views/accounts/show.html.erb
  41. +2 −0  app/views/addresses/create.js.rjs
  42. +10 −0 app/views/admin/servers/infos.html.erb
  43. +41 −0 app/views/bitcoin_transfers/new.html.erb
  44. +39 −0 app/views/charts/price.svg.builder
  45. +3 −0  app/views/informations/economy.html.erb
  46. +82 −0 app/views/informations/faq.html.erb
  47. +4 −0 app/views/informations/support.html.erb
  48. +13 −0 app/views/layouts/_errors.html.erb
  49. +17 −0 app/views/layouts/_flash.html.erb
  50. +6 −0 app/views/layouts/_footer.html.erb
  51. +31 −0 app/views/layouts/_navigation.html.erb
  52. +20 −0 app/views/layouts/_user_informations.html.erb
  53. +33 −0 app/views/layouts/application.html.erb
  54. +20 −0 app/views/liberty_reserve_transfers/_lr_sci_form.html.erb
  55. +49 −0 app/views/liberty_reserve_transfers/new.html.erb
  56. +11 −0 app/views/sessions/forbidden.html.erb
  57. +28 −0 app/views/sessions/new.html.erb
  58. +49 −0 app/views/trade_orders/book.html.erb
  59. +35 −0 app/views/trade_orders/index.html.erb
  60. +47 −0 app/views/trade_orders/new.html.erb
  61. +3 −0  app/views/trades/all_trades.json.erb
  62. +10 −0 app/views/trades/all_trades.xml.builder
  63. +33 −0 app/views/trades/index.html.erb
  64. +24 −0 app/views/transfers/index.html.erb
  65. +21 −0 app/views/user_mailer/registration_confirmation.html.erb
  66. +2 −0  app/views/user_mailer/registration_confirmation.text.erb
  67. +72 −0 app/views/users/_form.html.erb
  68. +2 −0  app/views/users/edit.html.erb
  69. +27 −0 app/views/users/index.html.erb
  70. +7 −0 app/views/users/new.html.erb
  71. +4 −0 config.ru
  72. +26 −0 config/application.rb
  73. +11 −0 config/bitcoin.yml
  74. +13 −0 config/boot.rb
  75. +19 −0 config/database.yml
  76. +54 −0 config/deploy.rb
  77. +5 −0 config/environment.rb
  78. +26 −0 config/environments/development.rb
  79. +10 −0 config/environments/production.rb
  80. +38 −0 config/environments/test.rb
  81. +7 −0 config/initializers/backtrace_silencers.rb
  82. +3 −0  config/initializers/donations.rb
  83. +10 −0 config/initializers/inflections.rb
  84. +3 −0  config/initializers/liberty_reserve.rb
  85. +7 −0 config/initializers/mime_types.rb
  86. +4 −0 config/initializers/recaptcha_keys.rb
  87. +7 −0 config/initializers/secret_token.rb
  88. +8 −0 config/initializers/session_store.rb
  89. +20 −0 config/liberty_reserve.yml
  90. +265 −0 config/locales/en.yml
  91. +181 −0 config/locales/fr.yml
  92. +7 −0 config/recaptcha.yml
  93. +58 −0 config/routes.rb
  94. +3 −0  config/schedule.rb
  95. +16 −0 db/migrate/20101017190414_add_sessions_table.rb
  96. +19 −0 db/migrate/20101017194104_create_users.rb
  97. +22 −0 db/migrate/20101017230001_create_transfers.rb
  98. +9 −0 db/migrate/20101022090523_add_last_address_column.rb
  99. +9 −0 db/migrate/20101123114028_add_currency_to_transfers.rb
  100. +13 −0 db/migrate/20101124101527_add_lr_metadata.rb
  101. +9 −0 db/migrate/20101125091027_add_salt_to_users.rb
  102. +13 −0 db/migrate/20101125140724_add_btc_meta_data.rb
  103. +34 −0 db/migrate/20101130120618_create_trade_orders.rb
  104. +9 −0 db/migrate/20101220214804_add_lr_fields.rb
  105. +9 −0 db/migrate/20101222002654_add_time_zone_to_users.rb
  106. +32 −0 db/migrate/20101222014309_create_trades.rb
  107. +9 −0 db/migrate/20101222020005_add_payee_id_to_transfers.rb
  108. +9 −0 db/migrate/20101223013915_add_cancelled_at_to_trade_orders.rb
  109. +10 −0 db/migrate/20101229231455_add_active_to_trade_order.rb
  110. +11 −0 db/migrate/20101229232404_add_ids_to_trades.rb
  111. +9 −0 db/migrate/20101230150344_remove_status_from_trade_orders.rb
  112. +79 −0 db/schema.rb
  113. +8 −0 db/seeds.rb
  114. +2 −0  doc/README_FOR_APP
  115. +21 −0 lib/bitcoin/client.rb
  116. +39 −0 lib/bitcoin/json_wrapper.rb
  117. +16 −0 lib/bitcoin/util.rb
  118. +92 −0 lib/liberty_reserve/client.rb
  119. 0  lib/tasks/.gitkeep
  120. +6 −0 lib/tasks/bitcoin.rake
  121. +7 −0 lib/validators/bitcoin_address_validator.rb
  122. +7 −0 lib/validators/minimal_amount_validator.rb
  123. +7 −0 lib/validators/minimal_order_amount_validator.rb
  124. +7 −0 lib/validators/minimal_order_ppc_validator.rb
  125. +13 −0 lib/validators/not_mine_validator.rb
  126. +7 −0 lib/validators/user_balance_validator.rb
  127. +26 −0 public/404.html
  128. +26 −0 public/422.html
  129. +26 −0 public/500.html
  130. BIN  public/favicon.ico
  131. +24 −0 public/graphs/sample.svg
  132. BIN  public/images/bitcoin-logo.jpg
  133. BIN  public/images/bitcoin.png
  134. BIN  public/images/delete.png
  135. BIN  public/images/liberty_reserve.gif
  136. BIN  public/images/liqpay.gif
  137. BIN  public/images/refresh.png
  138. +22 −0 public/javascripts/application.js
  139. +965 −0 public/javascripts/controls.js
  140. +974 −0 public/javascripts/dragdrop.js
  141. +1,123 −0 public/javascripts/effects.js
  142. +6,001 −0 public/javascripts/prototype.js
  143. +175 −0 public/javascripts/rails.js
  144. +5 −0 public/robots.txt
  145. 0  public/stylesheets/.gitkeep
  146. +430 −0 public/stylesheets/application.css
  147. +7 −0 public/stylesheets/reset.css
  148. +6 −0 script/rails
  149. 0  test/fixtures/transfers.yml
  150. +13 −0 test/fixtures/users.yml
  151. +4 −0 test/functional/accounts_controller_test.rb
  152. +8 −0 test/functional/addresses_controller_test.rb
  153. +8 −0 test/functional/admin/server_controller_test.rb
  154. +8 −0 test/functional/bitcoin_withdrawals_controller_test.rb
  155. +8 −0 test/functional/charts_controller_test.rb
  156. +8 −0 test/functional/informations_controller_test.rb
  157. +8 −0 test/functional/liberty_reserve_transfer_controller_test.rb
  158. +8 −0 test/functional/liberty_reserve_withdrawals_controller_test.rb
  159. +29 −0 test/functional/sessions_controller_test.rb
  160. +54 −0 test/functional/trade_orders_controller_test.rb
  161. +8 −0 test/functional/trades_controller_test.rb
  162. +4 −0 test/functional/transfers_controller_test.rb
  163. +8 −0 test/functional/user_mailer_test.rb
  164. +4 −0 test/functional/users_controller_test.rb
  165. +5 −0 test/performance/browsing_test.rb
  166. +21 −0 test/test_helper.rb
  167. +37 −0 test/unit/bitcoin_address_validator_test.rb
  168. +4 −0 test/unit/helpers/accounts_helper_test.rb
  169. +4 −0 test/unit/helpers/addresses_helper_test.rb
  170. +4 −0 test/unit/helpers/admin/server_helper_test.rb
  171. +4 −0 test/unit/helpers/bitcoin_transfers_helper_test.rb
  172. +4 −0 test/unit/helpers/bitcoin_withdrawals_helper_test.rb
  173. +4 −0 test/unit/helpers/charts_helper_test.rb
  174. +4 −0 test/unit/helpers/informations_helper_test.rb
  175. +4 −0 test/unit/helpers/liberty_reserve_transfer_helper_test.rb
  176. +4 −0 test/unit/helpers/liberty_reserve_withdrawals_helper_test.rb
  177. +4 −0 test/unit/helpers/sessions_helper_test.rb
  178. +4 −0 test/unit/helpers/trade_orders_helper_test.rb
  179. +4 −0 test/unit/helpers/trades_helper_test.rb
  180. +4 −0 test/unit/helpers/transfers_helper_test.rb
  181. +4 −0 test/unit/helpers/users_helper_test.rb
  182. +376 −0 test/unit/trade_order_test.rb
  183. +8 −0 test/unit/trade_test.rb
  184. +20 −0 test/unit/transfer_test.rb
  185. +4 −0 test/unit/user_test.rb
9 .gitignore
@@ -0,0 +1,9 @@
+.bundle
+db/*.sqlite3
+log/*.log
+tmp/**/*
+nbproject
+nbproject/*
+nbproject/**/*
+Thumbs.db
+**/Thumbs.db
4 Capfile
@@ -0,0 +1,4 @@
+load 'deploy' if respond_to?(:namespace) # cap2 differentiator
+Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
+
+load 'config/deploy' # remove this line to skip loading any of the default tasks
21 Gemfile
@@ -0,0 +1,21 @@
+source 'http://rubygems.org'
+
+gem 'rails', '3.0.0'
+
+gem 'mysql', '2.8.1'
+
+gem 'json'
+
+gem 'addressable'
+
+gem 'recaptcha',
+ :git => 'git://github.com/davout/recaptcha.git',
+ :require => 'recaptcha/rails'
+
+gem "exception_notification",
+ :git => "git://github.com/rails/exception_notification",
+ :require => 'exception_notifier'
+
+gem 'whenever'
+
+gem 'capistrano'
115 Gemfile.lock
@@ -0,0 +1,115 @@
+GIT
+ remote: git://github.com/davout/recaptcha.git
+ revision: 2e3bfac0a0e29dcbd0e77262d54264949df1d8d6
+ specs:
+ recaptcha (0.2.3)
+
+GIT
+ remote: git://github.com/rails/exception_notification
+ revision: 192a49a02d63d28b23ed41cebadfedd490929cf1
+ specs:
+ exception_notification (1.0.0)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ aaronh-chronic (0.3.9)
+ abstract (1.0.0)
+ actionmailer (3.0.0)
+ actionpack (= 3.0.0)
+ mail (~> 2.2.5)
+ actionpack (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ erubis (~> 2.6.6)
+ i18n (~> 0.4.1)
+ rack (~> 1.2.1)
+ rack-mount (~> 0.6.12)
+ rack-test (~> 0.5.4)
+ tzinfo (~> 0.3.23)
+ activemodel (3.0.0)
+ activesupport (= 3.0.0)
+ builder (~> 2.1.2)
+ i18n (~> 0.4.1)
+ activerecord (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ arel (~> 1.0.0)
+ tzinfo (~> 0.3.23)
+ activeresource (3.0.0)
+ activemodel (= 3.0.0)
+ activesupport (= 3.0.0)
+ activesupport (3.0.0)
+ addressable (2.2.2)
+ arel (1.0.1)
+ activesupport (~> 3.0.0)
+ builder (2.1.2)
+ capistrano (2.5.19)
+ highline
+ net-scp (>= 1.0.0)
+ net-sftp (>= 2.0.0)
+ net-ssh (>= 2.0.14)
+ net-ssh-gateway (>= 1.0.0)
+ erubis (2.6.6)
+ abstract (>= 1.0.0)
+ highline (1.6.1)
+ i18n (0.4.2)
+ json (1.4.6)
+ json (1.4.6-x86-mingw32)
+ mail (2.2.12)
+ activesupport (>= 2.3.6)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.16)
+ mysql (2.8.1)
+ mysql (2.8.1-x86-mingw32)
+ net-scp (1.0.4)
+ net-ssh (>= 1.99.1)
+ net-sftp (2.0.5)
+ net-ssh (>= 2.0.9)
+ net-ssh (2.0.23)
+ net-ssh-gateway (1.0.1)
+ net-ssh (>= 1.99.1)
+ polyglot (0.3.1)
+ rack (1.2.1)
+ rack-mount (0.6.13)
+ rack (>= 1.0.0)
+ rack-test (0.5.6)
+ rack (>= 1.0)
+ rails (3.0.0)
+ actionmailer (= 3.0.0)
+ actionpack (= 3.0.0)
+ activerecord (= 3.0.0)
+ activeresource (= 3.0.0)
+ activesupport (= 3.0.0)
+ bundler (~> 1.0.0)
+ railties (= 3.0.0)
+ railties (3.0.0)
+ actionpack (= 3.0.0)
+ activesupport (= 3.0.0)
+ rake (>= 0.8.4)
+ thor (~> 0.14.0)
+ rake (0.8.7)
+ thor (0.14.6)
+ treetop (1.4.9)
+ polyglot (>= 0.3.1)
+ tzinfo (0.3.23)
+ whenever (0.6.2)
+ aaronh-chronic (>= 0.3.9)
+ activesupport (>= 2.3.4)
+
+PLATFORMS
+ ruby
+ x86-mingw32
+
+DEPENDENCIES
+ addressable
+ capistrano
+ exception_notification!
+ json
+ mysql (= 2.8.1)
+ rails (= 3.0.0)
+ recaptcha!
+ whenever
256 README
@@ -0,0 +1,256 @@
+== Welcome to Rails
+
+Rails is a web-application framework that includes everything needed to create
+database-backed web applications according to the Model-View-Control pattern.
+
+This pattern splits the view (also called the presentation) into "dumb"
+templates that are primarily responsible for inserting pre-built data in between
+HTML tags. The model contains the "smart" domain objects (such as Account,
+Product, Person, Post) that holds all the business logic and knows how to
+persist themselves to a database. The controller handles the incoming requests
+(such as Save New Account, Update Product, Show Post) by manipulating the model
+and directing data to the view.
+
+In Rails, the model is handled by what's called an object-relational mapping
+layer entitled Active Record. This layer allows you to present the data from
+database rows as objects and embellish these data objects with business logic
+methods. You can read more about Active Record in
+link:files/vendor/rails/activerecord/README.html.
+
+The controller and view are handled by the Action Pack, which handles both
+layers by its two parts: Action View and Action Controller. These two layers
+are bundled in a single package due to their heavy interdependence. This is
+unlike the relationship between the Active Record and Action Pack that is much
+more separate. Each of these packages can be used independently outside of
+Rails. You can read more about Action Pack in
+link:files/vendor/rails/actionpack/README.html.
+
+
+== Getting Started
+
+1. At the command prompt, create a new Rails application:
+ <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
+
+2. Change directory to <tt>myapp</tt> and start the web server:
+ <tt>cd myapp; rails server</tt> (run with --help for options)
+
+3. Go to http://localhost:3000/ and you'll see:
+ "Welcome aboard: You're riding Ruby on Rails!"
+
+4. Follow the guidelines to start developing your application. You can find
+the following resources handy:
+
+* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
+* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
+
+
+== Debugging Rails
+
+Sometimes your application goes wrong. Fortunately there are a lot of tools that
+will help you debug it and get it back on the rails.
+
+First area to check is the application log files. Have "tail -f" commands
+running on the server.log and development.log. Rails will automatically display
+debugging and runtime information to these files. Debugging info will also be
+shown in the browser on requests from 127.0.0.1.
+
+You can also log your own messages directly into the log file from your code
+using the Ruby logger class from inside your controllers. Example:
+
+ class WeblogController < ActionController::Base
+ def destroy
+ @weblog = Weblog.find(params[:id])
+ @weblog.destroy
+ logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
+ end
+ end
+
+The result will be a message in your log file along the lines of:
+
+ Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
+
+More information on how to use the logger is at http://www.ruby-doc.org/core/
+
+Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
+several books available online as well:
+
+* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
+* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
+
+These two books will bring you up to speed on the Ruby language and also on
+programming in general.
+
+
+== Debugger
+
+Debugger support is available through the debugger command when you start your
+Mongrel or WEBrick server with --debugger. This means that you can break out of
+execution at any point in the code, investigate and change the model, and then,
+resume execution! You need to install ruby-debug to run the server in debugging
+mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
+
+ class WeblogController < ActionController::Base
+ def index
+ @posts = Post.find(:all)
+ debugger
+ end
+ end
+
+So the controller will accept the action, run the first line, then present you
+with a IRB prompt in the server window. Here you can do things like:
+
+ >> @posts.inspect
+ => "[#<Post:0x14a6be8
+ @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
+ #<Post:0x14a6620
+ @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
+ >> @posts.first.title = "hello from a debugger"
+ => "hello from a debugger"
+
+...and even better, you can examine how your runtime objects actually work:
+
+ >> f = @posts.first
+ => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
+ >> f.
+ Display all 152 possibilities? (y or n)
+
+Finally, when you're ready to resume execution, you can enter "cont".
+
+
+== Console
+
+The console is a Ruby shell, which allows you to interact with your
+application's domain model. Here you'll have all parts of the application
+configured, just like it is when the application is running. You can inspect
+domain models, change values, and save to the database. Starting the script
+without arguments will launch it in the development environment.
+
+To start the console, run <tt>rails console</tt> from the application
+directory.
+
+Options:
+
+* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
+ made to the database.
+* Passing an environment name as an argument will load the corresponding
+ environment. Example: <tt>rails console production</tt>.
+
+To reload your controllers and models after launching the console run
+<tt>reload!</tt>
+
+More information about irb can be found at:
+link:http://www.rubycentral.com/pickaxe/irb.html
+
+
+== dbconsole
+
+You can go to the command line of your database directly through <tt>rails
+dbconsole</tt>. You would be connected to the database with the credentials
+defined in database.yml. Starting the script without arguments will connect you
+to the development database. Passing an argument will connect you to a different
+database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
+PostgreSQL and SQLite 3.
+
+== Description of Contents
+
+The default directory structure of a generated Ruby on Rails application:
+
+ |-- app
+ | |-- controllers
+ | |-- helpers
+ | |-- models
+ | `-- views
+ | `-- layouts
+ |-- config
+ | |-- environments
+ | |-- initializers
+ | `-- locales
+ |-- db
+ |-- doc
+ |-- lib
+ | `-- tasks
+ |-- log
+ |-- public
+ | |-- images
+ | |-- javascripts
+ | `-- stylesheets
+ |-- script
+ | `-- performance
+ |-- test
+ | |-- fixtures
+ | |-- functional
+ | |-- integration
+ | |-- performance
+ | `-- unit
+ |-- tmp
+ | |-- cache
+ | |-- pids
+ | |-- sessions
+ | `-- sockets
+ `-- vendor
+ `-- plugins
+
+app
+ Holds all the code that's specific to this particular application.
+
+app/controllers
+ Holds controllers that should be named like weblogs_controller.rb for
+ automated URL mapping. All controllers should descend from
+ ApplicationController which itself descends from ActionController::Base.
+
+app/models
+ Holds models that should be named like post.rb. Models descend from
+ ActiveRecord::Base by default.
+
+app/views
+ Holds the template files for the view that should be named like
+ weblogs/index.html.erb for the WeblogsController#index action. All views use
+ eRuby syntax by default.
+
+app/views/layouts
+ Holds the template files for layouts to be used with views. This models the
+ common header/footer method of wrapping views. In your views, define a layout
+ using the <tt>layout :default</tt> and create a file named default.html.erb.
+ Inside default.html.erb, call <% yield %> to render the view using this
+ layout.
+
+app/helpers
+ Holds view helpers that should be named like weblogs_helper.rb. These are
+ generated for you automatically when using generators for controllers.
+ Helpers can be used to wrap functionality for your views into methods.
+
+config
+ Configuration files for the Rails environment, the routing map, the database,
+ and other dependencies.
+
+db
+ Contains the database schema in schema.rb. db/migrate contains all the
+ sequence of Migrations for your schema.
+
+doc
+ This directory is where your application documentation will be stored when
+ generated using <tt>rake doc:app</tt>
+
+lib
+ Application specific libraries. Basically, any kind of custom code that
+ doesn't belong under controllers, models, or helpers. This directory is in
+ the load path.
+
+public
+ The directory available for the web server. Contains subdirectories for
+ images, stylesheets, and javascripts. Also contains the dispatchers and the
+ default HTML files. This should be set as the DOCUMENT_ROOT of your web
+ server.
+
+script
+ Helper scripts for automation and generation.
+
+test
+ Unit and functional tests along with fixtures. When using the rails generate
+ command, template test files will be generated for you and placed in this
+ directory.
+
+vendor
+ External libraries that the application depends on. Also includes the plugins
+ subdirectory. If the app has frozen rails, those gems also go here, under
+ vendor/rails/. This directory is in the load path.
7 Rakefile
@@ -0,0 +1,7 @@
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+require 'rake'
+
+BitcoinBank::Application.load_tasks
2  app/controllers/accounts_controller.rb
@@ -0,0 +1,2 @@
+class AccountsController < ApplicationController
+end
5 app/controllers/addresses_controller.rb
@@ -0,0 +1,5 @@
+class AddressesController < ApplicationController
+ def create
+ @current_user.generate_new_address
+ end
+end
5 app/controllers/admin/servers_controller.rb
@@ -0,0 +1,5 @@
+class Admin::ServersController < ApplicationController
+ def infos
+ @infos = @bitcoin.get_info
+ end
+end
43 app/controllers/application_controller.rb
@@ -0,0 +1,43 @@
+class ApplicationController < ActionController::Base
+ protect_from_forgery
+
+ helper :all
+
+ before_filter :get_bitcoin_client,
+ :authenticate,
+ :authorize,
+ :set_time_zone,
+ :remove_params
+
+ def authenticate
+ if session[:current_user_id]
+ @current_user = User.find session[:current_user_id]
+ end
+ end
+
+ def authorize
+ unless @current_user
+ deny_request!
+ end
+ end
+
+ def deny_request!
+ render :template => 'sessions/forbidden',
+ :status => :forbidden
+ end
+
+ def get_bitcoin_client
+ @bitcoin = Bitcoin::Client.new
+ end
+
+ def set_time_zone
+ if @current_user and !@current_user.time_zone.blank?
+ Time.zone = ActiveSupport::TimeZone[@current_user.time_zone]
+ end
+ end
+
+ def remove_params
+ params.delete :skip_captcha
+ params.delete :skip_password
+ end
+end
23 app/controllers/bitcoin_transfers_controller.rb
@@ -0,0 +1,23 @@
+class BitcoinTransfersController < ApplicationController
+ def new
+ @bitcoin_transfer = BitcoinTransfer.new
+ end
+
+ def create
+ @bitcoin_transfer = @current_user.bitcoin_transfers.new(params[:bitcoin_transfer])
+
+ @bitcoin_transfer.withdrawal!
+ @bitcoin_transfer.currency = "BTC"
+
+ verify_recaptcha and @bitcoin_transfer.captcha_checked!
+
+ if @bitcoin_transfer.save
+ @bitcoin_transfer.execute!
+
+ redirect_to account_transfers_path,
+ :notice => "You successfuly transferred #{@bitcoin_transfer.amount.abs} BTC to the #{@bitcoin_transfer.address} bitcoin address"
+ else
+ render :action => :new
+ end
+ end
+end
48 app/controllers/charts_controller.rb
@@ -0,0 +1,48 @@
+class ChartsController < ApplicationController
+ skip_before_filter :authenticate, :authorize
+
+ before_filter :set_defaults
+
+ after_filter :set_content_type
+
+ layout false
+
+ def price
+# @prices = [
+# {DateTime.now.advance(:hours => -48)}
+# ]
+
+ @prices = [
+ {DateTime.now.advance(:days => -7) => 0.21},
+ {DateTime.now => 0.23},
+ {DateTime.now.advance(:days => 1) => 0.19},
+ {DateTime.now.advance(:days => 2) => 0.20},
+ {DateTime.now.advance(:days => 3) => 0.21},
+ {DateTime.now.advance(:days => 4) => 0.22},
+ {DateTime.now.advance(:days => 5) => 0.15},
+ {DateTime.now.advance(:days => 6) => 0.23},
+ {DateTime.now.advance(:days => 7) => 0.235}
+ ]
+
+ min_price, max_price = 0.15, 0.23
+ @n_v_ticks = 10
+ @min_price = min_price - 0.3 * (max_price - min_price)
+ @max_price = max_price + 0.3 * (max_price - min_price)
+
+ @n_h_ticks = 15
+
+ @vertical_ticks = nil
+ @horizontal_ticks = nil
+ end
+
+ def set_defaults
+ @title = "Title"
+ @description = "Description"
+ @width = 600
+ @height = 400
+ end
+
+ def set_content_type
+ headers["Content-Type"] = "image/svg+xml"
+ end
+end
7 app/controllers/informations_controller.rb
@@ -0,0 +1,7 @@
+class InformationsController < ApplicationController
+ skip_before_filter :authorize
+
+ def lr_api
+ raise LibertyReserve::Client.new.get_transaction.to_yaml
+ end
+end
50 app/controllers/liberty_reserve_transfers_controller.rb
@@ -0,0 +1,50 @@
+class LibertyReserveTransfersController < ApplicationController
+ skip_before_filter :verify_authenticity_token, :get_bitcoin_client, :authenticate, :authorize, :set_time_zone,
+ :only => [:lr_create_from_sci]
+
+ def new
+ @liberty_reserve_transfer = LibertyReserveTransfer.new
+ end
+
+ def create
+ @liberty_reserve_transfer = @current_user.liberty_reserve_transfers.new(params[:liberty_reserve_transfer])
+
+ verify_recaptcha and @liberty_reserve_transfer.captcha_checked!
+
+ @liberty_reserve_transfer.withdrawal!
+
+ if @liberty_reserve_transfer.save
+ @liberty_reserve_transfer.execute!
+
+ redirect_to account_transfers_path,
+ :notice =>"You successfuly transferred #{@liberty_reserve_transfer.amount.abs} #{@liberty_reserve_transfer.currency} to the #{@liberty_reserve_transfer.lr_account_id} Liberty Reserve account"
+ else
+ render :action => :new
+ end
+ end
+
+ # Liberty Reserve bounce URLs
+ def lr_transfer_success
+ transfer = LibertyReserveTransfer.find_by_lr_transaction_id(params[:lr_transfer])
+
+ redirect_to account_transfers_path,
+ :notice => "Successfully added #{transfer.amount} #{params[:lr_currency]} to your account (LR fees : #{transfer.lr_merchant_fee + params[:lr_fee_amnt].to_f} #{params[:lr_currency]})"
+ end
+
+ def lr_transfer_fail
+ flash[:error] = "Your #{params[:lr_amnt]} #{params[:lr_currency]} transfer failed."
+ redirect_to account_transfers_path
+ end
+
+ # Liberty Reserve callback
+ def lr_create_from_sci
+ lr_fields = %w{lr_paidto lr_paidby lr_amnt lr_fee_amnt lr_currency lr_transfer lr_store lr_timestamp lr_merchant_ref lr_encrypted lr_encrypted2 account_id}
+
+ @lr_confirmation = {}
+ lr_fields.each { |f| @lr_confirmation[f.to_sym] = params[f].to_s }
+
+ LibertyReserveTransfer.create_from_lr_post!(@lr_confirmation)
+
+ render :text => ""
+ end
+end
24 app/controllers/sessions_controller.rb
@@ -0,0 +1,24 @@
+class SessionsController < ApplicationController
+ skip_filter :authorize, :only => [:new, :create]
+
+ def create
+ user = User.find :first,
+ :conditions => ['account = ? OR email = ?', params[:account].strip, params[:account].strip]
+
+ if (user and user.check_password(params[:password]) and verify_recaptcha)
+ session[:current_user_id] = user.id
+ flash[:notice] = "You logged-in successfully to your account."
+ redirect_to root_path
+ else
+ flash.now[:error] = "Authentication failed, check your credentials and the captcha answer"
+ render :action => 'new'
+ end
+ end
+
+ def destroy
+ reset_session
+
+ redirect_to new_session_path,
+ :notice => "You logged out successfully"
+ end
+end
37 app/controllers/trade_orders_controller.rb
@@ -0,0 +1,37 @@
+class TradeOrdersController < ApplicationController
+ skip_before_filter :authorize, :only => :book
+
+ def new
+ @trade_order = TradeOrder.new
+ end
+
+ def create
+ @trade_order = TradeOrder.new(params[:trade_order])
+ @trade_order.user = @current_user
+
+ if @trade_order.save
+ @trade_order.execute!
+
+ redirect_to account_trade_orders_path,
+ :notice => "Your trade order was created successfully"
+ else
+ render :action => :new
+ end
+ end
+
+ def index
+ @trade_orders = @current_user.trade_orders
+ end
+
+ def destroy
+ @current_user.trade_orders.find(params[:id]).destroy
+
+ redirect_to account_trade_orders_path,
+ :notice => "Trade order deleted successfully"
+ end
+
+ def book
+ @sales = TradeOrder.active_with_category(:sell).all
+ @purchases = TradeOrder.active_with_category(:buy).all
+ end
+end
11 app/controllers/trades_controller.rb
@@ -0,0 +1,11 @@
+class TradesController < ApplicationController
+ skip_before_filter :authenticate, :authorize, :only => :all_trades
+
+ def index
+ @trades = @current_user.purchase_trades + @current_user.sale_trades
+ end
+
+ def all_trades
+ @trades = Trade.all
+ end
+end
5 app/controllers/transfers_controller.rb
@@ -0,0 +1,5 @@
+class TransfersController < ApplicationController
+ def index
+ @transfers = @current_user.transfers.all
+ end
+end
40 app/controllers/users_controller.rb
@@ -0,0 +1,40 @@
+class UsersController < ApplicationController
+ skip_before_filter :authenticate, :authorize,
+ :only => [:new, :create]
+
+ def new
+ @user = User.new
+ end
+
+ def edit
+ @user = @current_user
+ end
+
+ def create
+ @user = User.new(params[:user])
+
+ verify_recaptcha and @user.captcha_checked!
+
+ if @user.save
+ session[:current_user_id] = @user.id
+ redirect_to account_path, :notice => 'Your account was successfully created'
+ else
+ render :action => :new
+ end
+ end
+
+ def update
+ @user = @current_user
+
+ params[:user].delete(:account)
+
+ verify_recaptcha and @user.captcha_checked!
+
+ if @user.update_attributes(params[:user])
+ redirect_to edit_user_path,
+ :notice => 'Your account was successfully updated'
+ else
+ render :action => :edit
+ end
+ end
+end
2  app/helpers/accounts_helper.rb
@@ -0,0 +1,2 @@
+module AccountsHelper
+end
2  app/helpers/addresses_helper.rb
@@ -0,0 +1,2 @@
+module AddressesHelper
+end
2  app/helpers/admin/server_helper.rb
@@ -0,0 +1,2 @@
+module Admin::ServerHelper
+end
31 app/helpers/application_helper.rb
@@ -0,0 +1,31 @@
+module ApplicationHelper
+ def menu_item(*args, &block)
+ options = args.extract_options!
+
+ if (options[:logged_in] and @current_user) or options[:logged_in].nil?
+ content_tag :li do
+ args[0] or block.call
+ end
+ end
+ end
+
+ def number_to_bitcoins(amount, options = {})
+ number_to_currency(amount, options.merge({:unit => "BTC"}))
+ end
+
+ def number_to_lrusd(amount, options = {})
+ number_to_currency(amount, options.merge({:unit => "LRUSD"}))
+ end
+
+ def number_to_lreur(amount, options = {})
+ number_to_currency(amount, options.merge({:unit => "LREUR"}))
+ end
+
+ def amount_field_value(amount)
+ if amount.blank? or amount.zero?
+ nil
+ else
+ amount.abs
+ end
+ end
+end
2  app/helpers/bitcoin_transfers_helper.rb
@@ -0,0 +1,2 @@
+module BitcoinTransfersHelper
+end
5 app/helpers/charts_helper.rb
@@ -0,0 +1,5 @@
+module ChartsHelper
+ def horizontal_axis(options)
+
+ end
+end
2  app/helpers/informations_helper.rb
@@ -0,0 +1,2 @@
+module InformationsHelper
+end
2  app/helpers/liberty_reserve_transfer_helper.rb
@@ -0,0 +1,2 @@
+module LibertyReserveTransferHelper
+end
2  app/helpers/sessions_helper.rb
@@ -0,0 +1,2 @@
+module SessionsHelper
+end
2  app/helpers/trade_orders_helper.rb
@@ -0,0 +1,2 @@
+module TradeOrdersHelper
+end
2  app/helpers/trades_helper.rb
@@ -0,0 +1,2 @@
+module TradesHelper
+end
2  app/helpers/transfers_helper.rb
@@ -0,0 +1,2 @@
+module TransfersHelper
+end
2  app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
+module UsersHelper
+end
12 app/mailers/user_mailer.rb
@@ -0,0 +1,12 @@
+class UserMailer < ActionMailer::Base
+ default :from => "Bitcoin Central support <support@bitcoin-central.net>"
+
+ def registration_confirmation(user)
+ @user = user
+
+ attachments.inline['bitcoin.png'] = File.read(File.join(Rails.root, "public", "images", "bitcoin.png"))
+
+ mail :to => user.email,
+ :subject => "Registration confirmation"
+ end
+end
88 app/models/bitcoin_transfer.rb
@@ -0,0 +1,88 @@
+class BitcoinTransfer < Transfer
+ belongs_to :payee,
+ :class_name => "User"
+
+ validates :address,
+ :bitcoin_address => true,
+ :not_mine => true
+
+ validates :currency,
+ :inclusion => { :in => ["BTC"] }
+
+ # An address is only mandatory when money is withdrawn
+ validate :address do
+ if amount.nil? or amount <= 0 # Outgoing transfer
+ unless internal
+ errors[:address] << "can't be blank" if address.blank?
+ end
+ end
+ end
+
+ def address=(a)
+ self[:address] = a.strip
+ end
+
+ def perform_transfers?
+ Rails.env == "production"
+ end
+
+ def execute!
+ raise "You can only execute an outgoing transfer!" if amount > 0
+
+ @bitcoin = Bitcoin::Client.new
+
+ @destination_account = payee_id || @bitcoin.get_account(address)
+
+ if @destination_account.blank?
+ # to_f = WTF, doesn't work without it...
+ update_attribute(:bt_tx_id, @bitcoin.send_from(user.id.to_s, address, amount.to_f.abs)) if perform_transfers?
+ else
+ BitcoinTransfer.create!(
+ :user_id => @destination_account.to_i,
+ :amount => amount.abs,
+ :currency => "BTC"
+ )
+
+ @bitcoin.move(user.id.to_s, @destination_account.to_s, amount.to_f.abs) if perform_transfers?
+ end
+ end
+
+ def confirmed?
+ (bt_tx_confirmations >= MIN_BTC_CONFIRMATIONS) or bt_tx_id.nil?
+ end
+
+ def self.synchronize_transactions!
+ # TODO : Handle weird edge case
+ # http://www.bitcoin.org/smf/index.php?topic=2404.0
+ @bitcoin = Bitcoin::Client.new
+
+ User.all.each do |u|
+ transactions = @bitcoin.list_transactions(u.id.to_s)
+
+ transactions = transactions.select do |tx|
+ ["receive", "generated"].include? tx["category"]
+ end
+
+ transactions.each do |tx|
+ t = BitcoinTransfer.find(
+ :first,
+ :conditions => ['bt_tx_id = ? AND user_id = ?', tx["txid"], u.id]
+ )
+
+ if t
+ t.bt_tx_confirmations = tx["confirmations"]
+ else
+ t = BitcoinTransfer.new(
+ :user_id => u.id,
+ :amount => tx["amount"],
+ :bt_tx_id => tx["txid"],
+ :bt_tx_confirmations => tx["confirmations"],
+ :currency => "BTC"
+ )
+ end
+
+ t.save!
+ end
+ end
+ end
+end
58 app/models/liberty_reserve_transfer.rb
@@ -0,0 +1,58 @@
+require 'digest'
+
+class LibertyReserveTransfer < Transfer
+ validates :currency,
+ :inclusion => { :in => ["LRUSD", "LREUR"] }
+
+ # An account ID is only mandatory when money is withdrawn
+ # TODO : How the hell could an amount be nil ? Must be a reason... can't remember
+ validate :lr_account_id do
+ if amount.nil? or amount <= 0 # Outgoing transfer
+ unless internal
+ errors[:lr_account_id] << "can't be blank" if lr_account_id.blank?
+ end
+ end
+ end
+
+ def execute!
+ result = LibertyReserve::Client.new.transfer(lr_account_id, amount.to_f.abs, currency)
+ self.lr_transaction_id = result['TransferResponse']['Receipt']['ReceiptId']
+ save(false)
+ end
+
+ def self.create_from_lr_post!(confirmation)
+ if valid_confirmation?(confirmation)
+ t = LibertyReserve::Client.new.get_transaction(confirmation[:lr_transfer])
+
+ transferred = t['HistoryResponse']['Receipt']['Amount'].to_f
+ merchant_fee = t['HistoryResponse']['Receipt']['Fee'].to_f
+ amount = transferred - merchant_fee
+
+ # TODO : Add originating account ID ?
+ create!(
+ :user_id => confirmation[:account_id],
+ :amount => amount,
+ :currency => confirmation[:lr_currency],
+ :lr_transaction_id => confirmation[:lr_transfer],
+ :lr_transferred_amount => transferred,
+ :lr_merchant_fee => merchant_fee
+ )
+ else
+ raise "Confirmation was invalid"
+ end
+ end
+
+ def self.valid_confirmation?(confirmation)
+ confirmation[:secret_word] = BitcoinBank::LibertyReserve['secret_word']
+ confirmation[:baggage] = "account_id=#{confirmation[:account_id]}"
+
+ confirmation_array = %w{lr_paidto lr_paidby lr_store lr_amnt lr_transfer lr_merchant_ref baggage lr_currency secret_word}.map do |f|
+ confirmation[f.to_sym]
+ end
+
+ confirmation_string = confirmation_array.join(":")
+ confirmation_hash = Digest::SHA2.new.update(confirmation_string).to_s.upcase
+
+ confirmation_hash == confirmation[:lr_encrypted2]
+ end
+end
76 app/models/trade.rb
@@ -0,0 +1,76 @@
+class Trade < ActiveRecord::Base
+ belongs_to :purchase_order,
+ :class_name => "TradeOrder"
+
+ belongs_to :sale_order,
+ :class_name => "TradeOrder"
+
+ belongs_to :seller,
+ :class_name => "User"
+
+ belongs_to :buyer,
+ :class_name => "User"
+
+ has_many :transfers
+
+ validates :purchase_order,
+ :presence => true
+
+ validates :sale_order,
+ :presence=> true
+
+ validates :traded_btc,
+ :numericality => true,
+ :presence => true
+
+ validates :traded_currency,
+ :numericality => true,
+ :presence => true
+
+ validates :ppc,
+ :numericality => true,
+ :presence => true
+
+ validates :currency,
+ :inclusion => { :in => ["LRUSD", "LREUR"] },
+ :presence => true
+
+ def execute!
+ # credit seller /w currency
+ transfers << LibertyReserveTransfer.create!(
+ :currency => currency,
+ :amount => traded_currency,
+ :user_id => sale_order.user_id,
+ :internal => true,
+ :skip_captcha => true,
+ :skip_password => true
+ )
+
+ # debit buyer of currency
+ transfers << LibertyReserveTransfer.create!(
+ :currency => currency,
+ :amount => -traded_currency,
+ :user_id => purchase_order.user_id,
+ :internal => true,
+ :skip_captcha => true,
+ :skip_password => true
+ )
+
+ # debit buyer of coins
+ transfers << sale = BitcoinTransfer.create!(
+ :currency => "BTC",
+ :amount => -traded_btc,
+ :user_id => sale_order.user_id,
+ :payee_id => purchase_order.user_id,
+ :internal => true,
+ :skip_captcha => true,
+ :skip_password => true
+ )
+
+ # Don't forget to update bitcoin internal accounting (which also
+ # automatically credits the buyer /w his coins)
+ sale.execute!
+
+ save!
+ end
+end
164 app/models/trade_order.rb
@@ -0,0 +1,164 @@
+class TradeOrder < ActiveRecord::Base
+ MIN_AMOUNT = 1.0
+
+ default_scope order('created_at DESC')
+
+ belongs_to :user
+
+ has_many :sale_trades,
+ :class_name => "Trade",
+ :foreign_key => :sale_order_id,
+ :dependent => :nullify
+
+ has_many :purchase_trades,
+ :class_name => "Trade",
+ :foreign_key => :purchase_order_id,
+ :dependent => :nullify
+
+ validates :user,
+ :presence => true
+
+ validates :amount,
+ :numericality => true
+
+ validate :amount do
+ if new_record?
+ if amount < MIN_AMOUNT
+ errors[:amount] << "must be greater than #{MIN_AMOUNT} BTC"
+ end
+
+ if (amount > user.balance(:btc)) and (category == "sell")
+ errors[:amount] << "is greater than your available balance (#{"%.4f" % user.balance(:btc)} BTC)"
+ end
+
+ if ((user.balance(currency) / ppc) < amount ) and (category == "buy")
+ errors[:amount] << "is greater than your buying capability at this price (#{"%.4f" % (user.balance(currency) / ppc)} BTC @ #{ppc} BTC/#{currency})"
+ end
+ end
+ end
+
+ validates :currency,
+ :presence => true,
+ :inclusion => { :in => ["LRUSD", "LREUR"] }
+
+ validates :category,
+ :presence => true,
+ :inclusion => { :in => ["buy", "sell"] }
+
+ validates :ppc,
+ :minimal_order_ppc => true,
+ :numericality => true
+
+ scope :matching_orders, lambda { |order|
+ # TESTME Check that the order scope is the specified one, not the default one
+ with_exclusive_scope do
+ where("category = ?", (order.category == 'buy' ? 'sell' : 'buy')).
+ active.
+ where("currency = ?", order.currency).
+ where("ppc #{(order.category == 'buy') ? '<=' : '>='} ? ", order.ppc).
+ where("user_id <> ?", order.user_id).
+ where("cancelled_at IS NULL").
+ order("ppc #{(order.category == 'buy') ? 'ASC' : 'DESC'}")
+ end
+ }
+
+ scope :active_with_category, lambda { |cat|
+ with_exclusive_scope do
+ where("category = ?", cat.to_s).
+ active.
+ order("ppc #{(cat.to_s == 'buy') ? 'DESC' : 'ASC'}")
+ end
+ }
+
+ scope :active, lambda {
+ where("active = ?", true)
+ }
+
+ def inactivate_if_needed!
+ if category == "sell"
+ self.active = false if (user.balance(:btc) < amount)
+ else
+ self.active = false if (user.balance(currency) < (amount * ppc))
+ end
+
+ save!
+ end
+
+ def execute!
+ TradeOrder.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE")
+
+ TradeOrder.transaction do
+ @rolled_back = false
+
+ begin
+ mos = TradeOrder.matching_orders(self)
+ mos.reverse!
+ mo = mos.pop
+
+ while !mo.blank? and active? and !destroyed?
+ is_purchase = category == "buy"
+ purchase, sale = (is_purchase ? self : mo), (is_purchase ? mo : self)
+
+ # We always take the the seller's PPC
+ p = sale.ppc
+
+ btc_amount = [
+ sale.amount, # Amount of BTC sold
+ purchase.amount, # Amount of BTC bought
+ sale.user.balance(:btc), # Seller's BTC balance
+ purchase.user.balance(currency) / p # Buyer's BTC buying power @ p
+ ].min
+
+ traded_btc = round_to(btc_amount, 5)
+ traded_currency = round_to(btc_amount * p, 5)
+
+ # Record the trade
+ trade = Trade.new(
+ :traded_btc => traded_btc,
+ :traded_currency => traded_currency,
+ :currency => currency,
+ :ppc => p,
+ :seller_id => sale.user_id,
+ :buyer_id => purchase.user_id,
+ :purchase_order_id => purchase.id,
+ :sale_order_id => sale.id
+ )
+
+ # Execute it
+ trade.execute!
+
+ # Update orders
+ mo.amount = mo.amount - traded_btc
+ self.amount = amount - traded_btc
+
+ # Inactivate orders if required
+ sale.active = false if (sale.user.balance(:btc) < sale.amount)
+ purchase.active = false if (purchase.user.balance(currency) < (purchase.amount * purchase.ppc))
+
+ # TODO : Split orders if an user has enough funds to partially honor an order ?
+ # Destroy or save them according to the remaining balance
+ [self, mo].each do |o|
+ if o.amount.zero?
+ o.destroy
+ else
+ o.save!
+ end
+ end
+
+ mo = mos.pop
+ end
+ rescue
+ @exception = $!
+ raise ActiveRecord::Rollback
+ ensure
+ raise @exception if @exception
+ end
+ end
+
+ TradeOrder.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ")
+ end
+
+ def round_to(arg, precision)
+ (arg * (10 ** precision)).round.to_f / (10 ** precision).to_f
+ end
+end
80 app/models/transfer.rb
@@ -0,0 +1,80 @@
+class Transfer < ActiveRecord::Base
+ MIN_BTC_CONFIRMATIONS = 3
+
+ default_scope order('created_at DESC')
+
+ after_save :inactivate_orders
+
+ attr_accessor :password, :captcha, :skip_captcha, :skip_password, :internal
+
+ belongs_to :user
+
+ belongs_to :trade
+
+ validates :user,
+ :presence => true
+
+ validates :amount,
+ :presence => true,
+ :numericality => true,
+ :user_balance => true,
+ :minimal_amount => true
+
+ validates :currency,
+ :presence => true
+
+ validate :password do
+ if !skip_password and !internal and (amount <= 0) and !user.check_password(password)
+ errors[:password] << "is invalid"
+ end
+ end
+
+ validate :captcha do
+ if !skip_captcha and !internal and captcha.nil? and new_record? and (amount <= 0)
+ errors[:captcha] << "answer is incorrect"
+ end
+ end
+
+ def type_name
+ type.gsub(/Transfer/, "").underscore.gsub(/\_/, " ").titleize
+ end
+
+ def captcha_checked!
+ self.captcha = true
+ end
+
+ def withdrawal!
+ self.amount = -(amount.abs) if amount
+ end
+
+ # Placeholder
+ def confirmed?
+ true
+ end
+
+ def skip_captcha!
+ @skip_captcha = true
+ end
+
+ def skip_password!
+ @skip_password = true
+ end
+
+ def internal!
+ @internal = true
+ end
+
+ def inactivate_orders
+ user.reload.trade_orders.each { |t| t.inactivate_if_needed! }
+ end
+
+ scope :with_currency, lambda { |currency|
+ where("transfers.currency = ?", currency.to_s.upcase)
+ }
+
+ scope :with_confirmations, lambda { |unconfirmed|
+ unless unconfirmed
+ where("currency <> 'BTC' OR bt_tx_confirmations >= ? OR amount <= 0 OR bt_tx_id IS NULL", MIN_BTC_CONFIRMATIONS)
+ end
+ }
+end
115 app/models/user.rb
@@ -0,0 +1,115 @@
+require 'digest'
+
+class User < ActiveRecord::Base
+ attr_accessor :new_password,
+ :new_password_confirmation,
+ :current_password,
+ :captcha,
+ :skip_captcha
+
+ before_save :update_password
+
+ before_create :generate_account_id
+
+ after_create :send_registration_confirmation
+
+ has_many :transfers,
+ :dependent => :destroy
+
+ has_many :bitcoin_transfers
+
+ has_many :liberty_reserve_transfers
+
+ has_many :trade_orders,
+ :dependent => :destroy
+
+ has_many :purchase_trades,
+ :class_name => "Trade",
+ :foreign_key => "buyer_id"
+
+ has_many :sale_trades,
+ :class_name => "Trade",
+ :foreign_key => "seller_id"
+
+ validates :account,
+ :uniqueness => true
+
+ validates :new_password,
+ :presence => true,
+ :length => { :minimum => 4},
+ :on => :create
+
+ validates :email,
+ :uniqueness => true,
+ :format => { :with => /[^\s]+@[^\s]{1,150}\.[a-zA-Z]{2,5}/} # Naive e-mail regexp :)
+
+ validate :current_password do
+ unless new_record?
+ errors[:current_password] << "is invalid" unless check_password(current_password)
+ end
+ end
+
+ validate :new_password do
+ unless new_password.blank?
+ unless new_password == new_password_confirmation
+ errors[:new_password] << "doesn't match its confirmation"
+ end
+ end
+ end
+
+ validate :captcha do
+ if captcha.nil? and new_record?
+ unless skip_captcha
+ errors[:captcha] << "answer is incorrect"
+ end
+ end
+ end
+
+ def captcha_checked!
+ self.captcha = true
+ end
+
+ def check_password(p)
+ unless p.blank?
+ self.password == Digest::SHA2.new.update(p + salt).to_s
+ end
+ end
+
+ def password=(p)
+ generate_salt!
+ self[:password] = Digest::SHA2.new.update(p + salt).to_s
+ end
+
+ def generate_salt!
+ self.salt = Digest::SHA2.new.update((rand * 10 ** 9).to_s).to_s
+ end
+
+ def generate_new_address
+ update_attribute(:last_address, Bitcoin::Client.new.get_new_address(id.to_s))
+ last_address
+ end
+
+ def skip_captcha!
+ @skip_captcha = true
+ end
+
+ def last_address
+ super or generate_new_address
+ end
+
+ def balance(currency, options = {} )
+ transfers.with_currency(currency).with_confirmations(options[:unconfirmed]).map(&:amount).sum
+ end
+
+ def update_password
+ self.password = new_password unless new_password.blank?
+ end
+
+ def generate_account_id
+ self.account = "BC-U#{"%06d" % (rand * 10 ** 6).to_i}"
+ end
+
+ def send_registration_confirmation
+ UserMailer.registration_confirmation(self).deliver
+ end
+end
86 app/views/accounts/show.html.erb
@@ -0,0 +1,86 @@
+<h1>
+ Account
+</h1>
+
+<table class="default" id="account-balances">
+ <tr>
+ <th />
+ <th>
+ Currency
+ </th>
+ <th>
+ Balance
+ </th>
+ </tr>
+
+ <tr>
+ <td>
+ <%= link_to image_tag("bitcoin-logo.jpg"), "http://www.bitcoin.org/", :target => "_blank", :title => "Bitcoin home" %>
+ </td>
+ <td>
+ BTC
+ </td>
+ <td class="account-balance">
+ <%
+ btc_balance = @current_user.balance(:btc)
+ unconfirmed_btc_balance = @current_user.balance(:btc, :unconfirmed => true) - btc_balance
+ %>
+
+ <span class="<%= @current_user.balance(:btc) > 0 ? "green" : (@current_user.balance(:btc) < 0 ? "red" : "") %>">
+ <%= btc_balance > 0 ? "+" : (btc_balance < 0 ? "-" : "") %> <%= number_to_currency btc_balance.abs, :unit => "" %>
+ </span>
+
+ <% unless unconfirmed_btc_balance.zero? %>
+ <br />
+ <span class="unconfirmed" title="These funds are awaiting confirmation from the bitcoin network">
+ + <%= number_to_currency unconfirmed_btc_balance.abs, :unit => "" %>
+ </span>
+ <% end %>
+ </td>
+ </tr>
+
+ <tr>
+ <td rowspan="2">
+ <%= link_to image_tag("liberty_reserve.gif"), "https://www.libertyreserve.com/", :target => "_blank", :title => "Liberty Reserve home" %>
+ </td>
+ <td>
+ LRUSD
+ </td>
+ <td class="account-balance <%= @current_user.balance(:lrusd) > 0 ? "green" : (@current_user.balance(:lrusd) < 0 ? "red" : "") %>">
+ <%= @current_user.balance(:lrusd) > 0 ? "+" : (@current_user.balance(:lrusd) < 0 ? "-" : "") %> <%= number_to_currency @current_user.balance(:lrusd).abs, :unit => "" %>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ LREUR
+ </td>
+ <td class="account-balance <%= @current_user.balance(:lreur) > 0 ? "green" : (@current_user.balance(:lreur) < 0 ? "red" : "") %>">
+ <%= @current_user.balance(:lreur) > 0 ? "+" : (@current_user.balance(:lreur) < 0 ? "-" : "") %> <%= number_to_currency @current_user.balance(:lreur).abs, :unit => "" %>
+ </td>
+ </tr>
+
+ <% if false %>
+ <!-- No LiqPay for now -->
+ <tr>
+ <td rowspan="2">
+ <%= link_to image_tag("liqpay.gif"), "https://www.liqpay.com/", :target => "_blank", :title => "LiqPAY home" %>
+ </td>
+ <td>
+ LPUSD
+ </td>
+ <td class="account-balance <%= @current_user.balance(:lpusd) > 0 ? "green" : (@current_user.balance(:lpusd) < 0 ? "red" : "") %>">
+ <%= @current_user.balance(:lpusd) > 0 ? "+" : (@current_user.balance(:lpusd) < 0 ? "-" : "") %> <%= number_to_currency @current_user.balance(:lpusd).abs, :unit => "" %>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ LPEUR
+ </td>
+ <td class="account-balance <%= @current_user.balance(:lpeur) > 0 ? "green" : (@current_user.balance(:lpeur) < 0 ? "red" : "") %>">
+ <%= @current_user.balance(:lpeur) > 0 ? "+" : (@current_user.balance(:lpeur) < 0 ? "-" : "") %> <%= number_to_currency @current_user.balance(:lpeur).abs, :unit => "" %>
+ </td>
+ </tr>
+ <% end %>
+</table>
2  app/views/addresses/create.js.rjs
@@ -0,0 +1,2 @@
+page['user-information'].replace render(:partial => 'layouts/user_informations')
+page['last-bitcoin-address'].highlight(:startcolor => "#FE9791", :endcolor => "#FFFFFF")
10 app/views/admin/servers/infos.html.erb
@@ -0,0 +1,10 @@
+<h1>Server informations</h1>
+
+<table id="server-infos">
+ <% @infos.each do |k,v| %>
+ <tr>
+ <th><%= k %></th>
+ <td><%= v %></td>
+ </tr>
+ <% end %>
+</table>
41 app/views/bitcoin_transfers/new.html.erb
@@ -0,0 +1,41 @@
+<h1>Deposit bitcoins</h1>
+
+<p>Simply transfer funds to any of your addresses and they'll show up in your account after a couple of minutes.</p>
+<p>Please note that you will only be able to use these funds after the bitcoin network has confirmed the transfer (approx. 20 minutes).</p>
+
+<h1>Transfer bitcoins</h1>
+
+<%= form_for @bitcoin_transfer, :url => account_bitcoin_transfers_path, :html => { :method => :post } do |f| %>
+ <%=
+ render :partial => 'layouts/errors', :locals => {
+ :model => @bitcoin_transfer,
+ :message => "prevented your funds from being sent"
+ }
+ %>
+
+ <div class="form-field">
+ <%= f.label :address %>
+ <%= f.text_field :address, :class => "bitcoin-address" %>
+ </div>
+
+ <div class="form-field">
+ <%= f.label :amount %>
+ <%= f.text_field :amount, :value => amount_field_value(@bitcoin_transfer.amount), :autocomplete => "off" %>
+ </div>
+
+ <div class="form-field">
+ <%= f.label :password %>
+ <%= f.password_field :password %>
+ </div>
+
+ <div class="recaptcha-field">
+ <label for="recaptcha_response_field">Are you human ?</label>
+ <div class="recaptcha-input">
+ <%= recaptcha_tags :ssl => true %>
+ </div>
+ </div>
+
+ <div class="form-field">
+ <%= f.submit :value => "Withdraw", :class => "submit" %>
+ </div>
+<% end %>
39 app/views/charts/price.svg.builder
@@ -0,0 +1,39 @@
+xml.instruct!
+
+xml.svg :xmlns => "http://www.w3.org/2000/svg" , :version => "1.1", :width => @width, :height => @height do
+ xml.title @title
+ xml.desc @description
+
+ # Axes
+ xml.polyline :points => "0,0 0,#{@height} #{@width},#{@height}",
+ :style => "fill:white;stroke:black;stroke-width:1"
+
+ # Vertical ticks
+ (1..@n_v_ticks).each do |tick|
+ y_pos = @height - ((@height / @n_v_ticks) * tick)
+ tick_value = (((@max_price - @min_price)/@n_v_ticks.to_f) * tick) + @min_price
+
+ xml.line :x1 => 0,
+ :x2 => @width,
+ :y1 => y_pos,
+ :y2 => y_pos,
+ :stroke => "#AAA",
+ :"stroke-width" => "0.1"
+
+ xml.text tick_value.to_s, :x => "5", :y => y_pos, :"font-size" => 10
+ end
+
+ # Horizontal time ticks
+ (1..@n_h_ticks).each do |tick|
+ x_pos = @width - ((@width / @n_h_ticks) * tick)
+ xml.line :x1 => x_pos,
+ :x2 => x_pos,
+ :y1 => 0,
+ :y2 => @height,
+ :stroke => "#AAA",
+ :"stroke-width" => "0.1"
+
+ xml.text tick_value.to_s, :x => "5", :y => y_pos, :"font-size" => 10
+ end
+end
+
3  app/views/informations/economy.html.erb
@@ -0,0 +1,3 @@
+<h1>Bitcoin economy summarized</h1>
+<h2>How many bitcoins are there ?</h2>
+<p>Currently there has been a total of <strong><%= number_to_currency (@bitcoin.get_block_count * 50), :precision => 0, :unit => '' %></strong> generated bitcoins.
82 app/views/informations/faq.html.erb
@@ -0,0 +1,82 @@
+<div id="faq">
+ <h1>Frequently asked questions</h1>
+ <div class="question">
+ <h2>What is Bitcoin?</h2>
+ <p>
+ Bitcoin is a peer-to-peer digital currency. Peer-to-peer means that there is no central authority to issue new money or keep track of transactions. Instead, these tasks are managed collectively by the nodes of the network. Advantages:
+ <ul>
+ <li>
+ Bitcoins can be sent easily through the Internet, without having to trust middlemen.
+ </li>
+ <li>
+ Transactions are designed to be irreversible.
+ </li>
+ <li>
+ Be safe from instability caused by fractional reserve banking and central banks. The limited inflation of the Bitcoin system’s money supply is distributed evenly (by CPU power) throughout the network, not monopolized by banks.
+ </li>
+ </ul>
+ More detailed explanations can be found at <strong><%= link_to "bitcoin.org", "http://bitcoin.org" %></strong>.
+ </p>
+ </div>
+
+ <div class="question">
+ <h2>There's no point in having a bank for decentralized currency!</h2>
+ <p>
+ The concept of an usual bank obviously doesn't apply for a decentralized and anonymous currency,
+ traditional banks mostly make their money by :
+ <ul>
+ <li>Paying interests on deposits,</li>
+ <li>Charging interests on loans,</li>
+ <li>and keeping the resulting difference.</li>
+ <li>They also make money by charging various service fees.</li>
+ </ul>
+ </p>
+ <p>
+ Using the bitcoin system there will obviously no loans granted since it won't be possible to hold
+ anyone liable for the resulting debt repayments.
+ </p>
+ </div>
+
+ <div class="question">
+ <h2>So? What is this concept based on?</h2>
+ <p>
+ Well, the whole bank-for-bitcoins concept is based on the idea that, eventually, lots of people will
+ buy goods and services using bitcoins, and most people not really caring about running bitcoin nodes.<br />
+ </p>
+ <p>
+ A bitcoin based bank would help addressing some bitcoin-specific issues :
+ </p>
+ <ul>
+ <li>Eliminate transaction confirmation delays when transactions are between two bank users,</li>
+ <li>Eliminate the need to back your wallet up, it's taken care of for you,</li>
+ <li>Provide an easy entry-point for bitcoin related trading,</li>
+ <li>Provide dead-easy tools and resources for merchants to implement bitcoin-enabled online shops,</li>
+ <li>Provide a much more user-friendly interface to people who don't really care about the bitcoin internals,</li>
+ <li>Provide functionality that would increase bitcoin adoption as a currency like sending bitcoins in e-mail for example.</li>
+ </ul>
+ </div>
+
+ <div class="question">
+ <h2>How are you going to make money?</h2>
+ <p>
+ I expect to make money in the long run by charging very small transaction fees when payments go to merchant accounts.
+ </p>
+ <p>
+ The amount that would be charged would be smaller than the average amount charged for bitcoin transactions which would make a
+ win-win-win deal between the two parties and the bank : faster transactions for cheaper !
+ </p>
+ <p>
+ As of now, everything is completely free!
+ </p>
+ </div>
+
+ <div class="question">
+ <h2>How can I add funds to my account?</h2>
+ <p>
+ You can simply transfer Liberty Reserve USD or EUR by using the relevant menu option.
+ </p>
+ <p>
+ You can also add bitcoins by simply transferring them to one of your bitcoin addresses (your latest address is in the upper right corner when you are signed-in).
+ </p>
+ </div>
+</div>
4 app/views/informations/support.html.erb
@@ -0,0 +1,4 @@
+<h1>Support</h1>
+<p>
+ Please feel free to send an e-mail to <a href="mailto:support@bitcoin-central.net">support@bitcoin-central.net</a> if you have any questions or comments.
+</p>
13 app/views/layouts/_errors.html.erb
@@ -0,0 +1,13 @@
+<% if model.errors.any? %>
+ <div id="errors">
+ <h2>
+ <%= pluralize(model.errors.count, "error") %> <%= message %> :
+ </h2>
+
+ <ul>
+ <% model.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
+<% end %>
17 app/views/layouts/_flash.html.erb
@@ -0,0 +1,17 @@
+<% if flash %>
+ <% unless flash[:error].blank? and flash[:notice].blank? %>
+ <div class="flash">
+ <% unless flash[:error].blank? %>
+ <div class="error">
+ <%= flash[:error].html_safe %>
+ </div>
+ <% end %>
+
+ <% unless flash[:notice].blank? %>
+ <div class="notice">
+ <%= flash[:notice].html_safe %>
+ </div>
+ <% end %>
+ </div>
+ <% end %>
+<% end %>
6 app/views/layouts/_footer.html.erb
@@ -0,0 +1,6 @@
+<div id="footer">
+ Feel free to support this project by donating a few BTC to
+ <span class="bitcoin-address">
+ <%= BitcoinBank::Donations %>
+ </span>
+</div>
31 app/views/layouts/_navigation.html.erb
@@ -0,0 +1,31 @@
+<ul id="menu">
+ <%= menu_item :logged_in => true do %>
+ <%= link_to "<span>Account</span>".html_safe, "#" %>
+ <ul>
+ <%= menu_item link_to("<span>Balance</span>".html_safe, account_path), :logged_in => true %>
+ <%= menu_item link_to("<span>History</span>".html_safe, account_transfers_path), :logged_in => true %>
+ <%= menu_item link_to("<span>Add/Send LR</span>".html_safe, new_account_liberty_reserve_transfer_path), :logged_in => true %>
+ <%= menu_item link_to("<span>Add/Send BTC</span>".html_safe, new_account_bitcoin_transfer_path), :logged_in => true %>
+ </ul>
+ <% end %>
+
+ <%= menu_item do %>
+ <%= link_to "<span>Trading</span>".html_safe, "#" %>
+ <ul>
+ <%= menu_item link_to("<span>New order</span>".html_safe, new_account_trade_order_path), :logged_in => true %>
+ <%= menu_item link_to("<span>Your orders</span>".html_safe, account_trade_orders_path), :logged_in => true %>
+ <%= menu_item link_to("<span>Order book</span>".html_safe, book_account_trade_orders_path) %>
+ <%= menu_item link_to("<span>Trade history</span>".html_safe, account_trades_path), :logged_in => true %>
+ <li><%= link_to "<span>Statistics</span>".html_safe, "#" %></li>
+ </ul>
+ <% end %>
+
+ <%= menu_item do %>
+ <%= link_to "<span>Help</span>".html_safe, "#" %>
+ <ul>
+ <li><%= link_to "<span>FAQ</span>".html_safe, faq_path %></li>
+ <li><%= link_to "<span>Economy</span>".html_safe, economy_path %></li>
+ <li><%= link_to "<span>Support</span>".html_safe, support_path %></li>
+ </ul>
+ <% end %>
+</ul>
20 app/views/layouts/_user_informations.html.erb
@@ -0,0 +1,20 @@
+<div id="user-information">
+ <p>
+ <% if @current_user %>
+ <p class="identification">
+ Signed-in as <%= link_to @current_user.account, edit_user_path %> - <%= link_to "Logout", session_path, :method => :delete %>
+ </p>
+ <p>
+ Your address : <span class="bitcoin-address" id="last-bitcoin-address"><%= @current_user.last_address %></span>
+ <%= link_to image_tag('refresh.png'),
+ user_addresses_path,
+ :remote => true,
+ :method => :post,
+ :id => 'refresh-button',
+ :title => 'Generate a new receiving address, refresh at will' %>
+ </p>
+ <% else %>
+ <%= link_to "Sign-up", new_user_path %> or <%= link_to "login", new_session_path %>
+ <% end %>
+ </p>
+</div>
33 app/views/layouts/application.html.erb
@@ -0,0 +1,33 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+ <meta name="keywords" lang="en" content="bitcoin, bitcoin central, buy bitcoins, sell bitcoins" />
+
+ <title>Bitcoin Central</title>
+
+ <%= stylesheet_link_tag %w{reset application}, :cache => "all" %>
+ <%= javascript_include_tag :defaults, :cache => "all" %>
+
+ <%= csrf_meta_tag %>
+ </head>
+ <body>
+ <div id="content">
+ <h1 id="main-title">
+ <%= link_to "<span>Bitcoin Central</span>".html_safe, root_path %>
+ </h1>
+
+ <%= render :partial => 'layouts/user_informations' %>
+ <%= render :partial => 'layouts/navigation' %>
+ <%= render :partial => 'layouts/flash', :object => flash %>
+
+ <div id="right-content">
+ <%= yield %>
+ </div>
+ </div>
+
+ <%= render :partial => 'layouts/footer' %>
+ </body>
+</html>
20 app/views/liberty_reserve_transfers/_lr_sci_form.html.erb
@@ -0,0 +1,20 @@
+<form id="add_lr" action="https://sci.libertyreserve.com/en" method="POST">
+ <input type="hidden" name="lr_acc" value="<%= BitcoinBank::LibertyReserve['account'] %>" />
+
+ <input type="hidden" name="lr_store" value="<%= BitcoinBank::LibertyReserve['store'] %>" />
+ <input type="hidden" name="lr_merchant_ref" value="<%= @current_user.id %>" />
+ <input type="hidden" name="lr_comments" value="Transfer funds to account <%= @current_user.account %>" />
+
+ <input type="hidden" name="lr_success_url" value="<%= BitcoinBank::LibertyReserve['success_url'] %>" />
+ <input type="hidden" name="lr_success_url_method" value="GET" />
+
+ <input type="hidden" name="lr_fail_url" value="<%= BitcoinBank::LibertyReserve['fail_url'] %>" />
+ <input type="hidden" name="lr_fail_url_method" value="GET" />
+
+ <input type="hidden" name="lr_status_url" value="<%= BitcoinBank::LibertyReserve['status_url'] %>" />
+ <input type="hidden" name="lr_status_url_method" value="POST" />
+
+ <input type="hidden" name="account_id" value="<%= @current_user.id %>" />
+</form>
+
+<a href="#" onclick="$('add_lr').submit();">Click here</a> to be taken to the Liberty Reserve interface, you'll be able to specify the details of your deposit.
49 app/views/liberty_reserve_transfers/new.html.erb
@@ -0,0 +1,49 @@
+<h1>Deposit Liberty Reserve</h1>
+
+<%= render :partial => 'lr_sci_form' %>
+
+<h1>Withdraw Liberty Reserve</h1>
+
+<%= form_for @liberty_reserve_transfer, :url => account_liberty_reserve_transfers_path do |f| %>
+ <%=
+ render :partial => 'layouts/errors', :locals => {
+ :model => @liberty_reserve_transfer,
+ :message => "prevented your funds from being sent"
+ }
+ %>
+
+ <div class="form-field">
+ <%= f.label :lr_account_id %>
+ <%= f.text_field :lr_account_id, :autocomplete => "off" %>
+ </div>
+
+ <div class="form-field">
+ <%= f.label :amount %>
+ <%= f.text_field :amount, :value => amount_field_value(@liberty_reserve_transfer.amount), :autocomplete => "off" %>
+ </div>
+
+ <div class="form-field">
+ <%= f.label :currency %>
+
+ <div class="radios">
+ <%= f.radio_button :currency, "LRUSD" %> <%= f.label :currency_lrusd, "USD" %>
+ <%= f.radio_button :currency, "LREUR" %> <%= f.label :currency_lreur, "EUR" %>
+ </div>
+ </div>
+
+ <div class="form-field">
+ <%= f.label :password %>
+ <%= f.password_field :password %>
+ </div>
+
+ <div class="recaptcha-field">
+ <label for="recaptcha_response_field">Are you human ?</label>
+ <div class="recaptcha-input">
+ <%= recaptcha_tags :ssl => true %>
+ </div>
+ </div>
+
+ <div class="form-field">
+ <%= f.submit :value => "Send", :class => "submit" %>
+ </div>
+<% end %>
11 app/views/sessions/forbidden.html.erb
@@ -0,0 +1,11 @@
+<h1>You must be logged-in to access this page</h1>
+
+<p>
+ Creating an account is <strong>fast</strong>, <strong>anonymous</strong> and <strong>painless</strong> !
+</p>
+
+<p>
+ You migt want to
+ <strong><%= link_to "login", new_session_path %></strong> or
+ <strong><%= link_to "create an account", new_user_path %></strong>.
+</p>
28 app/views/sessions/new.html.erb
@@ -0,0 +1,28 @@
+<%= form_tag session_path, :method => :post do %>
+ <div class="form-field">
+ <label for="account">Account ID</label>
+ <input name="account" id="account" />
+ <div class="explanation">
+ E-mail address or account ID
+ </div>
+ </div>
+
+ <div class="form-field">
+ <label for="password">Password</label>
+ <input name="password" id="password" type="password" />
+ <div class="explanation">
+ <a href="mailto:support@bitcoin-central.net" tabindex="-1">Password forgotten?</a>
+ </div>
+ </div>
+
+ <div class="recaptcha-field">
+ <label for="recaptcha_response_field">Are you human ?</label>
+ <div class="recaptcha-input">
+ <%= recaptcha_tags :ssl => true %>
+ </div>
+ </div>
+
+ <div class="form-field">
+ <input type="submit" class="submit" value="Sign-in" />
+ </div>
+<% end %>
49 app/views/trade_orders/book.html.erb