banano 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 11765178fb87fa12d30b11dbe6c454af09e87c2b2ba389e368669698809ee256
4
+ data.tar.gz: dee6f73a715b2b9e400491fbef8fdfbbb732051ad3d297e1dc49a3803306a0c9
5
+ SHA512:
6
+ metadata.gz: b1cca559663f4ec639fe847d5425c1babf9be16d0d8477af593b9034f3f2093e24e6ea109e7ae6a30b53cc9af3246f061c703d3e6b5f87f2c37ec5d7bebd9c7e
7
+ data.tar.gz: 37b0964c5ff8a431f403ef81f3eba4df3b44a9238026e3e6b28b993fccf8cd2aff1087ad386597a17a5f0bf1f83fac36ae4f2d193315d3a8ca4178aaea08ba4a
@@ -0,0 +1,2 @@
1
+ - Initial import
2
+ - Gem packaging
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 zhware
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,183 @@
1
+ # Banano
2
+
3
+ The current Gem trying to make as easy as possible working with [Banano currency](https://banano.cc/).
4
+ More information about the Banano currency networking can be found on the [Banano Currency Wiki pages](https://github.com/BananoCoin/banano/wiki/Network-Specifications).
5
+ The current library is still work in progress, but the basic functionallity is already implemented:
6
+
7
+ - Good part of the RPC protocol for working with Banano nodes
8
+ - Wallet, Account operations - send and receive payments etc.
9
+ - Conversion between units - RAW to Banano and Banano to RAW
10
+
11
+ Some parts of the library are heavily influenced by the great [nanook Ruby library](https://github.com/lukes/nanook) for working with the similar [NANO currency](https://nano.org/en/).
12
+
13
+ Everybody is welcome to contrubute to the project. Have fun and use Ruby and Banano.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'banano'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ ```sh
26
+ bundle install
27
+ ```
28
+
29
+ Or install it yourself as:
30
+
31
+ ```sh
32
+ gem install banano
33
+ ```
34
+
35
+ ## Usage
36
+
37
+ The code is divided on following the parts:
38
+
39
+ - `Banano::Unit` - conversion between [RAW and Banano units](https://nanoo.tools/banano-units)
40
+ - `Banano::Protocol` - including all other parts. Can be used also for some syntax sugar (to avoid sending node parameter to constructors)
41
+ - `Banano::Client` - very thin wrapper around [Faraday HTTP Client](https://github.com/lostisland/faraday) for easy JSON-based communications
42
+ - `Banano::Node` - Banano Node abstraction - mostly encapsulate JSON communications
43
+ - `Bananp::Wallet` - wallets holding for Banano accounts on the current node
44
+ - `Bananp::Account` - uniq 'ban_...' addresses used for currency tokens exchange
45
+ - `Bananp::WalletAccount` - link between `Account` and `Wallet` - check if account exists in the wallet etc.
46
+
47
+ ### Banano::Protocol
48
+
49
+ Usually everything starts here. By default the library will work with [locally running Banano node](https://github.com/BananoCoin/banano/wiki/Running-a-Docker-Bananode).
50
+ If it is impossible to be done, you can use the [Public bananode RPC API](https://nanoo.tools/bananode-api). Example below is for that case:
51
+
52
+ ```rb
53
+ BETA_URL = 'https://api-beta.banano.cc'
54
+ @anano = Banano::Protocol.new(uri: BETA_URL)
55
+ # From here it is easy to create other object, without sending URI to them
56
+ wallet = @banano.wallet('WALLET15263636...')
57
+ account = @banano.account('ban_1...')
58
+ ```
59
+ ### Banano::Client
60
+
61
+ Not used directly, better use `Banano::Node` instead
62
+
63
+ ```rb
64
+ client = Banano::Client.new(uri: BETA_URL)
65
+ client.rpc_call(action: :version)
66
+ ```
67
+
68
+ ### Banano::Node
69
+
70
+ Most of the information about the running node is encapsulated here. The other parts using mostly the `Banano::Node.rpc()` method, not the low level client one:
71
+
72
+ ```rb
73
+ Banano::Node.account_count # number of node accounts
74
+ Banano::Node.block_count # check here if there are still non-syncronized blocks
75
+ Banano::Node.peers # other nodes connected to that one
76
+ # accounts with voting power
77
+ Banano::Node.representatives
78
+ Banano::Node.representatives_online
79
+ # Is your node synced already
80
+ Banano::Node.synchronizing_blocks
81
+ Banano::Node.sync_progress
82
+ ```
83
+
84
+ ### Banano::Wallet
85
+
86
+ Wallets are like a bags of accounts. Accounts can be only local, created on the current node. They will not be visible for other nodes.
87
+
88
+ ```rb
89
+ wallet = Banano::Wallet.create # create new wallet
90
+ wallet.destroy # remove the wallet
91
+ wallet.export # export wallet to JSON
92
+ wallet.accounts # current wellet accounts
93
+ wallet.contains?('ban_1...') # check if the account exists in the current wallet
94
+ # Accounts with voting power
95
+ wallet.default_representative
96
+ wallet.change_default_representative('ban_1...')
97
+ wallet.change_password('SomePassword') # protect your wallet
98
+ walled.lock # no more payments
99
+ wallet.locked?
100
+ walled.unlock('SomePassword') # resume receiving payments
101
+ wallet.balance # check how many banano the whole wallet have, RAW units
102
+ wallet.balance(raw: false) # wallet balance in Banano units
103
+ wallet.balance(account_break_down: true) # banano per acount, RAW units
104
+ # Payments
105
+ wallet.pay(from: 'ban_1...', to: 'ban_3...', amount: '1.23', raw: false, id: 'x123')
106
+ wallet.pending(limit: 10, detailed: true) # some payments waiting wallet unlock
107
+ wallet.receive(into: 'ban_1') # receive the pending banano into some wallet account
108
+ wallet.restore(seed: 'XVVREGNN...') # restore some wallet on the current node
109
+ ```
110
+
111
+ ### Banano::Account
112
+
113
+ Account are holding units with unique address, where the banano tokens are accumulated. They can be local for the current node and not accessable for other nodes.
114
+
115
+ ```rb
116
+ account = Banano::Account(node: @banano.node, address: 'ban1_...') # create new account on that node
117
+ account.exists? # check if account exists
118
+ # some account attributes
119
+ account.last_modified_at
120
+ account.public_key
121
+ account.representative
122
+ account.balance # in RAW units
123
+ accunt.balance(raw: false) # in banano units
124
+ # Payments
125
+ account.pending(limit: 100, detailed: true) # detailed information about the pending payments
126
+ account.history(limit: 10) # the latest payments - send and receive
127
+ ```
128
+
129
+ ### Banano::WalletAccount
130
+
131
+ Because accounts and wallets so closly connected, some linkage object is very helpful.
132
+
133
+ ```rb
134
+ wallet = @banano.wallet('XBHHNN...')
135
+ # create wallet <-> accounts connection
136
+ wallet_acc = Banano::WalletAccount(node: @banano.node, wallet: wallet.id)
137
+ wallet_acc.create # create account in the wallet
138
+ wallet_acc.create(3) # create additional 3 accounts inside the same wallet
139
+ # Working with specific account
140
+ account = @banano.account('ban_1')
141
+ wallet_other_acc = Banano::WalletAccount(node: pbanano.node, wallet: wallet.id, account: account.id)
142
+ wallet_other_acc.pay(to: 'ban_1...', amount: 10, raw: false, id: 'x1234') # send some banano
143
+ wallet_other_acc.receive # receive some banano
144
+
145
+ ```
146
+
147
+ ### Banano::Unit
148
+
149
+ Using [Ruby bigdecimal library](https://apidock.com/ruby/BigDecimal) for all operations:
150
+
151
+ ```rb
152
+ Banano::Unit.ban_to_raw(1) # -> BigDecimal('100000000000000000000000000000')
153
+ Banano::Unit.raw_to_ban('1') # -> BigDecimal('0.00000000000000000000000000001')
154
+ ```
155
+
156
+ The library also is checking some currency related limits, for example total supply limit: `3402823669.20938463463374607431768211455` banano max
157
+
158
+ ## Development
159
+
160
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
161
+
162
+ Clone the gem source code and start experimenting:
163
+
164
+ ```sh
165
+ git clone https://github.com/zh/rbanano
166
+ ```
167
+
168
+ `rspec` is used for testing. To run all tests execute:
169
+
170
+ ```sh
171
+ bundle exec rspec spec
172
+ ```
173
+
174
+ Still a lot of tests needed. For now `Banano::Unit` and `Banano:Util` are ready. The other parts tests will be added soon.
175
+
176
+ ## Contributing
177
+
178
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/zh/rbanano](https://github.com/zh/rbanano).
179
+
180
+
181
+ ## License
182
+
183
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "banano"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.dirname(__FILE__) + '/banano/*.rb'].each {|file| require file }
4
+
5
+ module Banano
6
+ class Protocol
7
+ attr_reader :node
8
+
9
+ def initialize(uri: Client::LOCAL_ENDPOINT, timeout: Client::DEFAULT_TIMEOUT)
10
+ @node = Node.new(uri: uri, timeout: timeout)
11
+ end
12
+
13
+ # Returns a new instance of {Banano::Account}.
14
+ #
15
+ # ==== Example:
16
+ # account = Banano::Protocol.new.account(address: "ban_3e3j...")
17
+ #
18
+ # @param address [String] the id of the account you want to work with
19
+ # @return [Banano::Account]
20
+ def account(address)
21
+ Banano::Account.new(node: @node, address: address)
22
+ end
23
+
24
+ # Returns a new instance of {Nanook::Key}.
25
+ #
26
+ # ==== Example:
27
+ # key = Banano::Protocol.new.key("3068BB...")
28
+ #
29
+ # @param key [String] a private key
30
+ # @return [Banano::Key]
31
+ def key(key = nil)
32
+ Banano::Key.new(node: @node, key: key)
33
+ end
34
+
35
+ # Returns a new instance of {Banano::Wallet}.
36
+ #
37
+ # ==== Example:
38
+ # wallet = Banano::Protocol.new.wallet("000D1BAE...")
39
+ #
40
+ # @param wallet [String] the id of the wallet you want to work with
41
+ # @return [Banano::Wallet]
42
+ def wallet(wallet = nil)
43
+ Banano::Wallet.new(node: @node, wallet: wallet)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Banano
4
+ class Account
5
+ attr_accessor :address
6
+
7
+ def initialize(node:, address:)
8
+ @node = node
9
+ @address = address
10
+ end
11
+
12
+ # The last modified time of the account in the time zone of
13
+ # your node (usually UTC).
14
+ #
15
+ # ==== Example:
16
+ #
17
+ # account.last_modified_at # => Time
18
+ #
19
+ # @return [Time] last modified time of the account in the time zone of
20
+ # your nano node (usually UTC).
21
+ def last_modified_at
22
+ response = rpc(action: :account_info)
23
+ Time.at(response[:modified_timestamp].to_i)
24
+ end
25
+
26
+ # The public key of the account.
27
+ #
28
+ # ==== Example:
29
+ #
30
+ # account.public_key # => "3068BB1..."
31
+ #
32
+ # @return [String] public key of the account
33
+ def public_key
34
+ rpc(action: :account_key)[:key]
35
+ end
36
+
37
+ # The representative account id for the account.
38
+ # Representatives are accounts that cast votes in the case of a
39
+ # fork in the network.
40
+ #
41
+ # ==== Example:
42
+ #
43
+ # account.representative # => "ban_3pc..."
44
+ #
45
+ # @return [String] Representative account of the account
46
+ def representative
47
+ rpc(action: :account_representative)[:representative]
48
+ end
49
+
50
+ # The account's balance, including pending (unreceived payments).
51
+ # To receive a pending amount see {WalletAccount#receive}.
52
+ #
53
+ # ==== Examples:
54
+ #
55
+ # account.balance
56
+ #
57
+ # Example response:
58
+ #
59
+ # {
60
+ # balance: 2,
61
+ # pending: 1.1
62
+ # }
63
+ #
64
+ # @param raw [Boolean] if true raw, else banano units
65
+ # @raise ArgumentError if an invalid +unit+ was given.
66
+ # @return [Hash{Symbol=>Integer|Float}]
67
+ def balance(raw = true)
68
+ rpc(action: :account_balance).tap do |r|
69
+ unless raw == true
70
+ r[:balance] = Banano::Unit.raw_to_ban(r[:balance]).to_f
71
+ r[:pending] = Banano::Unit.raw_to_ban(r[:pending]).to_f
72
+ end
73
+ end
74
+ end
75
+
76
+ # @return [Integer] number of blocks for this account
77
+ def block_count
78
+ rpc(action: :account_block_count)[:block_count].to_i
79
+ end
80
+
81
+ # Information about pending blocks (payments) that are
82
+ # waiting to be received by the account.
83
+ #
84
+ # The default response is an Array of block ids.
85
+ #
86
+ # With the +detailed:+ argument, the method returns an Array of Hashes,
87
+ # which contain the source account id, amount pending and block id.
88
+ #
89
+ # ==== Examples:
90
+ #
91
+ # account.pending # => ["000D1BA..."]
92
+ #
93
+ # Asking for more detail to be returned:
94
+ #
95
+ # account.pending(detailed: true)
96
+ #
97
+ # @param limit [Integer] number of pending blocks to return (default is 1000)
98
+ # @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+)
99
+ # @param raw [Boolean] if true raw, else banano units
100
+ #
101
+ # @return [Array<String>]
102
+ # @return [Array<Hash{Symbol=>String|Integer}>]
103
+ def pending(limit: 1000, detailed: false, raw: true)
104
+ params = {count: limit}
105
+ params[:source] = true if detailed
106
+
107
+ response = rpc(action: :pending, params: params)[:blocks]
108
+ return response unless detailed && !response.empty?
109
+
110
+ response.map do |key, val|
111
+ p = val.merge(block: key.to_s)
112
+ p[:amount] = Banano::Unit.raw_to_ban(p[:amount]).to_f unless raw == true
113
+ p
114
+ end
115
+ end
116
+
117
+ # The id of the account.
118
+ #
119
+ # ==== Example:
120
+ #
121
+ # account.id # => "ban_16u..."
122
+ #
123
+ # @return [String] the id of the account
124
+ def id
125
+ @address
126
+ end
127
+
128
+ # Information about this accounts that have set this account as their representative.
129
+ #
130
+ # === Example:
131
+ #
132
+ # account.delegators
133
+ #
134
+ # @param raw [Boolean] if true return raw balances, else banano units
135
+ # @return [Hash{Symbol=>Integer}] account ids which delegate to this account
136
+ def delegators(raw = true)
137
+ response = rpc(action: :delegators)[:delegators]
138
+ return response if raw == true
139
+
140
+ r = response.map do |address, balance|
141
+ [address.to_s, Banano::Unit.raw_to_ban(balance).to_f]
142
+ end
143
+ Hash[r]
144
+ end
145
+
146
+ # Information about the account.
147
+ #
148
+ # ==== Examples:
149
+ #
150
+ # account.info
151
+ #
152
+ # @return [Hash{Symbol=>String|Integer|Float}] information about the account
153
+ def info
154
+ rpc(action: :account_info)
155
+ end
156
+
157
+ # Returns true if the account has an <i>open</i> block.
158
+ #
159
+ # An open block gets published when an account receives a payment
160
+ # for the first time.
161
+ #
162
+ # ==== Example:
163
+ #
164
+ # account.exists? # => true
165
+ # # or
166
+ # account.open? # => true
167
+ #
168
+ # @return [Boolean] Indicates if this account has an open block
169
+ def exists?
170
+ response = info
171
+ !response.empty? && !response[:open_block].nil?
172
+ end
173
+ alias open? exists?
174
+
175
+ # An account's history of send and receive payments.
176
+ #
177
+ # ==== Example:
178
+ #
179
+ # account.history
180
+ #
181
+ # @param limit [Integer] maximum number of history items to return
182
+ # @param raw [Boolean] raw or banano
183
+ # @return [Array<Hash{Symbol=>String}>] the history of send and receive payments for this account
184
+ def history(limit: 1000, raw: true)
185
+ response = Array(rpc(action: :account_history, params: {count: limit})[:history])
186
+ response = response.collect {|h| Banano::Util.symbolize_keys(h) }
187
+ return response if raw == true
188
+
189
+ response.map! do |history|
190
+ history[:amount] = Banano::Unit.raw_to_ban(history[:amount]).to_f
191
+ history
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ def rpc(action:, params: {})
198
+ p = @address.nil? ? {} : {account: @address}
199
+ @node.rpc(action: action, params: p.merge(params))
200
+ end
201
+ end
202
+ end